多線程編程學習筆記——使用異步IO

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

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

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

 

         假設如下場景,若是在客戶端運行程序,最的事情之一是有一個響應的用戶界面。這意味着不管應用程序發生什麼,全部的用戶界面元素都要保持 快速運行,用戶可以從應用程序獲得快速響應。達到這一點並不容易!若是你嘗試在Windows系統中打開記事本並加載一個有幾兆大小的文檔,應用程序窗口將交結一段的時間,由於整個文件要先從硬盤中加載,而後程序才能開始處理用戶輸入。網絡

        這是一個很是重要的問題,在這種狀況下,惟一方案是不管如何都要避免阻塞UI純種。這反過來意味着爲了防止阻塞UI線程,每一個與UI有關的API必須只被容許異步調用 。這是Windows操做系統從新升級API的關鍵緣由 ,其幾乎把每一個方法替換爲異步方式。可是應用程序使用多線程來達到此目的會影響性能嗎?固然會。然而考慮到只有一個用戶,那麼這是划算的。若是應用程序可使用電腦的全部能力從而變得更加高效,並且這種能力 只爲運行程序的惟一用戶服務,這是好事。多線程

         接下來看看第二種狀況。若是程序運行在服務器端,則是徹底不一樣的情形。可伸縮性是最高優先級,這意味着單個 用戶消耗越少的資源越好。若是爲每一個用戶建立多個線程,則可伸縮性並很差。以高效的方式來平衡應用程序資源的消耗是個很是複雜的問題。例如,在ASP.NET中,咱們使用工做線程池來服務客戶端請求。這個池的工做線程是有限的,因此不得不最小化每一個工做線程的使用時間以便達到高伸縮性。這意味着須要把工做線程越快越好地放回到池中,從而能夠服務下一個請求。若是咱們啓動了一個須要計算的異步操做,則整個工做流程會很低效。首先從線程池中取出一個工做 線程用以服務客戶端請求。而後取出另外一個工做線程並開始處理異步操做。如今有兩個工做線程都在處理請求,若是第一個線程能作些有用的事則很是好。惋惜,一般狀況下,咱們簡單等待異步 操做完成,可是咱們卻消費了兩個工做 線程,而不是一個。在這個場景中,異步 比同步執行實際上更糟糕!咱們不須要使用全部CPU核心,由於咱們已經在服務不少客戶端,它們已經使用了CPU的全部計算能力。咱們無須保持第一個線程響應,由於這沒有用戶界面。那麼爲何咱們應該在服務端使用異步呢?併發

        答案是隻有異步輸入/輸出操做才應用使用異步。目前,現代計算機經過有一個磁盤驅動器來存儲文件,一塊網卡來經過網絡發送與接收數據。全部這些設備都有本身的芯片,以很是底層的方式來管理輸入/輸出操做併發信號 給操做系統。這種執行I/O任務的方式被稱爲I/O線程。dom

          在ASP.NET中,一旦有一個異步的I/O操做在工做線程開始時,它會被當即返回到線程池中。當這個操做繼續運行時,這個線程能夠服務其餘的客戶端。最終,當操做發出信號完成時,ASP.NET基礎設施從線程池中獲取一個空閒的工做線程,而後會完成這個操做。異步

1、   異步使用文件async

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

 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。

      而後並行地異步地從全部文件中讀取數據,統計每一個文件內容,而後求總和。

        最後,刪除全部文件。

相關文章
相關標籤/搜索