zoukankan      html  css  js  c++  java
  • [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作

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

    序  

      上次,博主通过《利用 async & await 的异步编程》这篇点睛之作初步介绍了 async & await 的基本用法及异步的控制流和一些其它的东西;  

      接着,博主通过《怎样使用 async & await 一步步将同步代码转换为异步编程》这篇随笔诱导大家如何一步步转换自己的同步代码;

      今天,我们来一起看看如何用异步进行 IO 操作。

    目录

    • 使用异步特性实现 IO 操作的意义
    • 使用带异步的 FileStream 
    • 异步写入文本
    • 异步读取文本
    • 并行异步 I/O

    使用异步特性实现 IO 操作的意义

    • 异步特性有利于增强 App 的响应能力。因为一个操作的 UI 线程可以执行其他工作。如果 UI 线程需要执行较长时间的代码(如 > 1s),UI 会阻塞到 I/O 完成,这时用户界面线程才可以重新处理键盘、鼠标输入和其他操作。
    • 在本地进行文件访问也许效率非常高,但是,假如该文件在远程的服务器上呢?
    • 使用异步额外增加的开销很小,不大。
    • 异步任务可以并行运行。

    使用带异步的 FileStream 

      这里使用 FileStream 类,它带有一个参数 useAsync,可以避免在许多情况下阻塞线程池的线程。可以通过 useAsync = true 来进行启用或在构造函数中进行参数调用。

      但是我们不能对 StreamReader 和 StreamWriter 中的参数进行设置。但是,如果你想使用该参数 useAsync,则需要自己新建一个 FileStream 对象。请注意,异步调用是在 UI 中的,即使线程池线程阻塞,在 await 期间,用户界面线程也不会被阻塞。

    异步写入文本

      下面的示例是将文本写入到文件。在每个 await 语句中,都会立即退出。当文件 I/O 完成时,方法会执行 await 语句后面的语句。请注意异步修饰符在使用 await 语句方法的定义。

    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)返回任务并导致进程的文件。使用等待的第二个语句(行号3、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
  • 相关阅读:
    Cmder安装和设置
    php7.x版本的新特性
    【Luogu】P4916 [MtOI2018]魔力环 题解
    Codeforces 1530E Minimax 题解
    昭阳E42-80 屏幕不亮,风扇狂转
    iPad宽高像素值
    关于UIView的autoresizingMask属性的研究
    判断单链表中是否存在环及查找环的入口点
    网络编程
    事件响应者链的工作原理
  • 原文地址:https://www.cnblogs.com/liqingwen/p/6082673.html
Copyright © 2011-2022 走看看