[.NET] 利用 async & await 進行異步 IO 操做

利用 async & await 進行異步 IO 操做

【博主】反骨仔  【出處】http://www.cnblogs.com/liqingwen/p/6082673.html   html

序  

  上次,博主經過《利用 async & await 的異步編程》這篇點睛之做初步介紹了 async & await 的基本用法及異步的控制流和一些其它的東西;  編程

  接着,博主經過《怎樣使用 async & await 一步步將同步代碼轉換爲異步編程》這篇隨筆誘導你們如何一步步轉換本身的同步代碼;服務器

  今天,咱們來一塊兒看看如何用異步進行 IO 操做。app

 

目錄

  • 使用異步特性實現 IO 操做的意義
  • 使用帶異步的 FileStream 
  • 異步寫入文本
  • 異步讀取文本
  • 並行異步 I/O

 

使用異步特性實現 IO 操做的意義

  • 異步特性有利於加強 App 的響應能力。由於一個操做的 UI 線程能夠執行其餘工做。若是 UI 線程須要執行較長時間的代碼(如 > 1s),UI 會阻塞到 I/O 完成,這時用戶界面線程才能夠從新處理鍵盤、鼠標輸入和其餘操做。
  • 在本地進行文件訪問也許效率很是高,可是,假如該文件在遠程的服務器上呢?
  • 使用異步額外增長的開銷很小,不大。
  • 異步任務能夠並行運行。

 

使用帶異步的 FileStream 

  這裏使用 FileStream 類,它帶有一個參數 useAsync,能夠避免在許多狀況下阻塞線程池的線程。能夠經過 useAsync = true 來進行啓用或在構造函數中進行參數調用。異步

  可是咱們不能對 StreamReader 和 StreamWriter 中的參數進行設置。可是,若是你想使用該參數 useAsync,則須要本身新建一個 FileStream 對象。請注意,異步調用是在 UI 中的,即便線程池線程阻塞,在 await 期間,用戶界面線程也不會被阻塞。async

 

異步寫入文本

  下面的示例是將文本寫入到文件。在每一個 await 語句中,都會當即退出。當文件 I/O 完成時,方法會執行 await 語句後面的語句。請注意異步修飾符在使用 await 語句方法的定義。ide

1         private async void btnWrite_Click(object sender, RoutedEventArgs e)
2         {
3             await WriteTextAsync();
4         }
btnWrite_Click

 

 1         /// <summary>
 2         /// 異步寫入文件
 3         /// </summary>
 4         /// <returns></returns>
 5         private async Task WriteTextAsync()
 6         {
 7             var path = $"temp.txt";
 8             var content = Guid.NewGuid().ToString();
 9 
10             using (var fs = new FileStream(path,
11                 FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
12             {
13                 var buffer = Encoding.UTF8.GetBytes(content);
14 
15                 //var writeTask = fs.WriteAsync(buffer, 0, buffer.Length);
16                 //await writeTask;
17                 await fs.WriteAsync(buffer, 0, buffer.Length);
18             }
19         }

  行號 17 的語句能夠修改成:異步編程

1   //await fs.WriteAsync(buffer, 0, buffer.Length);
2   //能夠改成
3   var writeTask = fs.WriteAsync(buffer, 0, buffer.Length);
4   await writeTask;

  第一個語句(行號 1)返回任務並致使進程的文件。使用等待的第二個語句(行號三、4)致使方法當即退出並返回其餘任務。當隨後處理的文件完成時,執行回 await 的語句。 函數

 

異步讀取文本

  下面的示例是從文件中讀取文本。將文本緩衝區的內容放入 StringBuilder。不一樣於在前面的示例,會不斷 await 一個讀取的長度值。ReadAsync 方法返回 Task<Int32>,即 Task<int>,所以,等待的計算生成一個 Int32 值(numRead),在操做完成以後。性能

 1         /// <summary>
 2         /// 異步讀取文本
 3         /// </summary>
 4         /// <param name="fileName"></param>
 5         /// <returns></returns>
 6         private async Task<string> ReadTextAsync(string fileName)
 7         {
 8             using (var fs = new FileStream(fileName,
 9                 FileMode.OpenOrCreate, FileAccess.Read, FileShare.None, bufferSize: 4096, useAsync: true))
10             {
11                 var sb = new StringBuilder();
12                 var buffer = new byte[0x1000];  //十六進制 等於十進制的 4096
13                 var readLength = 0;
14 
15                 while ((readLength = await fs.ReadAsync(buffer, 0, buffer.Length)) != 0)
16                 {
17                     var text = Encoding.UTF8.GetString(buffer, 0, readLength);
18                     sb.Append(text);
19                 }
20 
21                 return sb.ToString();
22             }
23         }

 

 1         private async void btnRead_Click(object sender, RoutedEventArgs e)
 2         {
 3             var fileName = $"temp.txt";
 4             if (!File.Exists(fileName))
 5             {
 6                 Debug.WriteLine($"文件找不到:{fileName}");
 7                 return;
 8             }
 9 
10             try
11             {
12                 var content = await ReadTextAsync(fileName);
13                 Debug.WriteLine(content);
14             }
15             catch (Exception ex)
16             {
17                 Debug.WriteLine(ex.Message);
18             }
19         }
btnRead_Click

 

並行異步 I/O

  下面的示例經過編寫多個文本文件進行演示並行處理。對於每一個文件,WriteAsync 方法返回後將被添加到任務列表的集合中。在處理完成全部的任務時,await Task.WhenAll(tasks); 語句將退出方法並恢復執行。

  在任務完成後,進入 finally 塊的將全部 FileStream 實例進行清理回收。若是直接在 using 語句中建立 FileStream 實例,則 FileStream 實例可能在任務完成以前就被處理。

  【注意】全部性能提升幾乎徹底是異步並行處理。異步的優勢是它不會佔用多個線程,也就是說,它不會佔用用戶界面線程。

 1         /// <summary>
 2         /// 異步寫入多個文件
 3         /// </summary>
 4         /// <param name="folder"></param>
 5         /// <returns></returns>
 6         private async Task WriteMultiTextAsync(string folder)
 7         {
 8             var tasks = new List<Task>();
 9             var fileStreams = new List<FileStream>();
10 
11             try
12             {
13                 for (int i = 1; i <= 10; i++)
14                 {
15                     var fileName = Path.Combine(folder, $"{i}.txt");
16                     var content = Guid.NewGuid().ToString();
17                     var buffer = Encoding.UTF8.GetBytes(content);
18 
19                     var fs = new FileStream(fileName,
20         FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true);
21                     fileStreams.Add(fs);
22 
23                     var writeTask = fs.WriteAsync(buffer, 0, buffer.Length);
24                     tasks.Add(writeTask);
25                 }
26 
27                 await Task.WhenAll(tasks);
28             }
29             finally
30             {
31                 foreach (var fs in fileStreams)
32                 {
33                     fs.Close();
34                     fs.Dispose();
35                 }
36             }
37         }

 

 1         private async void btnWriteMulti_Click(object sender, RoutedEventArgs e)
 2         {
 3             var folder = $"temp";
 4 
 5             if (!Directory.Exists(folder))
 6             {
 7                 Directory.CreateDirectory(folder);
 8             }
 9 
10             await WriteMultiTextAsync(folder);
11         }
btnWriteMulti_Click

  在使用 WriteAsync 和 ReadAsync 方案時,你能夠指定 CancellationToken,來在中途取消操做。 

 

同系列的隨筆

 

 


 【參考】https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/concepts/async/using-async-for-file-access
相關文章
相關標籤/搜索