多線程編程學習筆記——使用非同步IO(一)

来源:https://www.cnblogs.com/chillsrc/archive/2018/01/26/8359641.html
-Advertisement-
Play Games

假設以下場景,如果在客戶端運行程式,最的事情之一是有一個響應的用戶界面。這意味著無論應用程式發生什麼,所有的用戶界面元素都要保持 快速運行,用戶能夠從應用程式得到快速響應。達到這一點並不容易!如果你嘗試在Windows系統中打開記事本並載入一個有幾兆大小的文檔,應用程式視窗將交結一段的時間,因為整個... ...


接上文 多線程編程學習筆記——使用併發集合(一)

接上文 多線程編程學習筆記——使用併發集合(二)

接上文 多線程編程學習筆記——使用併發集合(三)

 

         假設以下場景,如果在客戶端運行程式,最的事情之一是有一個響應的用戶界面。這意味著無論應用程式發生什麼,所有的用戶界面元素都要保持 快速運行,用戶能夠從應用程式得到快速響應。達到這一點並不容易!如果你嘗試在Windows系統中打開記事本並載入一個有幾兆大小的文檔,應用程式視窗將交結一段的時間,因為整個文件要先從硬碟中載入,然後程式才能開始處理用戶輸入。

        這是一個非常重要的問題,在這種情況下,唯一方案是無論如何都要避免阻塞UI純種。這反過來意味著為了防止阻塞UI線程,每個與UI有關的API必須只被允許非同步調用 。這是Windows操作系統重新升級API的關鍵原因 ,其幾乎把每個方法替換為非同步方式。但是應用程式使用多線程來達到此目的會影響性能嗎?當然會。然而考慮到只有一個用戶,那麼這是划算的。如果應用程式可以使用電腦的所有能力從而變得更加高效,而且這種能力 只為運行程式的唯一用戶服務,這是好事。

         接下來看看第二種情況。如果程式運行在伺服器端,則是完全不同的情形。可伸縮性是最高優先順序,這意味著單個 用戶消耗越少的資源越好。如果為每個用戶創建多個線程,則可伸縮性並不好。以高效的方式來平衡應用程式資源的消耗是個非常複雜的問題。例如,在ASP.NET中,我們使用工作線程池來服務客戶端請求。這個池的工作線程是有限的,所以不得不最小化每個工作線程的使用時間以便達到高伸縮性。這意味著需要把工作線程越快越好地放回到池中,從而可以服務下一個請求。如果我們啟動了一個需要計算的非同步操作,則整個工作流程會很低效。首先從線程池中取出一個工作 線程用以服務客戶端請求。然後取出另一個工作線程並開始處理非同步操作。現在有兩個工作線程都在處理請求,如果第一個線程能做些有用的事則非常好。可惜,通常情況下,我們簡單等待非同步 操作完成,但是我們卻消費了兩個工作 線程,而不是一個。在這個場景中,非同步 比同步執行實際上更糟糕!我們不需要使用所有CPU核心,因為我們已經在服務很多客戶端,它們已經使用了CPU的所有計算能力。我們無須保持第一個線程響應,因為這沒有用戶界面。那麼為什麼我們應該在服務端使用非同步呢?

        答案是只有非同步輸入/輸出操作才應用使用非同步。目前,現代電腦通過有一個磁碟驅動器來存儲文件,一塊網卡來通過網路發送與接收數據。所有這些設備都有自己的晶元,以非常底層的方式來管理輸入/輸出操作併發信號 給操作系統。這種執行I/O任務的方式被稱為I/O線程。

          在ASP.NET中,一旦有一個非同步的I/O操作在工作線程開始時,它會被立即返回到線程池中。當這個操作繼續運行時,這個線程可以服務其他的客戶端。最終,當操作發出信號完成時,ASP.NET基礎設施從線程池中獲取一個空閑的工作線程,然後會完成這個操作。

一、   非同步使用文件

      本救命學習如何使用非同步的方式讀寫一個文件。

 1.示例代碼如下。

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ThreadIODemo
{
    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("--開始 使用 非同步 I/O 線程 -- ");
            var t = ReadWriteAsyncIO();
            t.GetAwaiter().GetResult();
            Console.Read();
        }

        const int BUFFER_SIZE = 4096;

        async static Task ReadWriteAsyncIO()
        {

            using (var fs = new FileStream("test1.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None, BUFFER_SIZE))
            {

                Console.WriteLine("1. 使用 I/O 線程 是否非同步:{0}",fs.IsAsync);
                byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent());

                var writeTask = Task.Factory.FromAsync(fs.BeginWrite, fs.EndWrite, buffer, 0, buffer.Length, null);
                await writeTask;

            }

            using (var fs = new FileStream("test2.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None,
BUFFER_SIZE, FileOptions.Asynchronous)) { Console.WriteLine(
"2. 使用 I/O 線程 是否非同步:{0}",fs.IsAsync); byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent()); var writeTask = Task.Factory.FromAsync(fs.BeginWrite, fs.EndWrite, buffer, 0, buffer.Length, null); await writeTask; } using (var fs = File.Create("test3.txt", BUFFER_SIZE, FileOptions.Asynchronous)) { using (var sw = new StreamWriter(fs)) { Console.WriteLine("3. 使用 I/O 線程 是否非同步:{0}",fs.IsAsync); await sw.WriteAsync(CreateFileContent()); } } using (var sw = new StreamWriter("test4.txt", true)) { Console.WriteLine("4. 使用 I/O 線程 是否非同步:{0}",((FileStream)sw.BaseStream).IsAsync); await sw.WriteAsync(CreateFileContent()); } System.Threading.Thread.Sleep(1000); Console.WriteLine("開始非同步讀取文件"); Task<long>[] readTasks = new Task<long>[4]; for (int i = 0; i < 4; i++) { readTasks[i] = SumFileContent(string.Format("test{0}.txt",i + 1)); } long[] sums = await Task.WhenAll(readTasks); Console.WriteLine("所有文件中的和值:{0}", sums.Sum()); Console.WriteLine("開始刪除文件"); Task[] delTasks = new Task[4]; for (int i = 0; i < 4; i++) { string filename = string.Format("test{0}.txt",i + 1); delTasks[i] = SimulateAsynchronousDelete(filename); } await Task.WhenAll(delTasks); Console.WriteLine("刪除文件結束"); } static string CreateFileContent() { var sb = new StringBuilder(); for (int i = 0; i < 100000; i++) { sb.AppendFormat("{0}", new Random(i).Next(0, 99999)); sb.AppendLine(); } return sb.ToString(); } async static Task<long> SumFileContent(string filename) { using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous)) using (var sr = new StreamReader(fs)) { long sum = 0; while (sr.Peek() > -1) { string line = await sr.ReadLineAsync(); sum += long.Parse(line); } return sum; } } static Task SimulateAsynchronousDelete(string filename) { return Task.Run(() => File.Delete(filename)); } } }

 

2.程式運行結果,如下圖。

 

      當程式運行時,我們以不同的方式創建了4個文件,並寫入一些隨機數據。

      在第一個例子中,使用的是FileStream類以及其方式,將非同步編程模式API轉換成任務。

      在第二個例子中,使用的是FileStream類以及其方式,不過在構造的時候提供了FileStream.Asynchronous參數 。

      在第三個例子使用了一些簡化的API,比如File.Create方法和StreamWrite類。它也使用I/O線程,我們可以使用Stream.iSaSYNC屬性來檢查。

     在第四個例子說明瞭過分簡化 也不好。這裡我們藉助非同步委托調用來模擬非同步I/O,其實並沒有使用非同步I/O。

      然後並行地非同步地從所有文件中讀取數據,統計每個文件內容,然後求總和。

        最後,刪除所有文件。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 storm和kafka集群安裝是沒有必然聯繫的,我將這兩個寫在一起,是因為他們都是由zookeeper進行管理的,也都依賴於JDK的環境,為了不重覆再寫一遍配置,所以我將這兩個寫在一起。若只需一個,只需挑選自己選擇的閱讀即可。 這兩者的依賴如下: Storm集群 :JDK1.8 , Zooke ...
  • 印象中迴圈刪除list中的元素使用for迴圈的方式是有問題的,但是可以使用增強的for迴圈,然後今天在使用時發現報錯了,然後去科普了一下,再然後發現這是一個誤區。下麵就來講一講。。伸手黨可直接跳至文末。看總結。。 JAVA中迴圈遍歷list有三種方式for迴圈、增強for迴圈(也就是常說的forea ...
  • 我找到一些把字元串首字元大寫的方法。 <! more 假如需要把字元串 "red" 轉換為 "Red",把 "red house" 轉為 "Red house" 或者單詞的第一個大寫,下麵就是我從網上看到的技術。 這個方法就是拿到第一個字元,然後加上後面的字元,可以看到這個方法需要三個字元串在記憶體。 ...
  • 一、url地址傳參的第一種寫法 1.通過mvc中預設的url地址書寫格式:控制器/方法名/參數 2.實例:http://localhost:39270/RequestDemo/Index/88,預設參數名為id所以名稱為id。 如果使用其他名稱,後臺是無法讀取的會報錯 二、url地址傳參的第二種寫法 ...
  • 1、什麼是網路爬蟲 關於爬蟲百度百科這樣定義的:網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取萬維網信息的程式或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模擬程式或者蠕蟲。從搜索引擎開始,爬蟲應該就出現了,爬蟲所做的事情 ...
  • 最近在開發應用的過程中,我遇到瞭如標題所述的需求,其實主要是為了能夠快捷啟動應用,正像我們可以在“運行”對話框中可以輸入一些可執行程式的名稱後,就能夠直接啟動它;這樣做,可以增加 App 的易用性。在查了一些文檔後,得知在 Windows Build 16266 之後,就加入相關的 API,因此要實 ...
  • cmd regedit打開註冊表,進入到[HKEY_USERS\.DEFAULT\Control Panel\International] ,然後1、將鍵 sDate 的值由 / 改為 - 2、將鍵 sShortDate 的值由 yyyy/M/d 改為 yyyy-M-d (註:如果是yyyy/M/d ...
  • 這是一個使用js實現登錄、緩衝動畫以及頁面跳轉的例子。不需要用到資料庫、不需要複雜的邏輯、更不需要千行的代碼;只需要幾個簡單的標簽、幾張GIF格式的圖片、再加上幾行代碼就能實現以上功能。本文主要有三個HTML頁面構成,一個登錄頁面、一個載入緩衝頁面、最後一個是緩衝過後的首頁。為了做這個GIF圖,隨即 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...