【博主】反骨仔 【出處】http://www.cnblogs.com/liqingwen/p/6082673.html html
上次,博主經過《利用 async & await 的異步編程》這篇點睛之做初步介紹了 async & await 的基本用法及異步的控制流和一些其它的東西; 編程
接着,博主經過《怎樣使用 async & await 一步步將同步代碼轉換爲異步編程》這篇隨筆誘導你們如何一步步轉換本身的同步代碼;服務器
今天,咱們來一塊兒看看如何用異步進行 IO 操做。app
這裏使用 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 }
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 }
在任務完成後,進入 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 }
在使用 WriteAsync 和 ReadAsync 方案時,你能夠指定 CancellationToken,來在中途取消操做。