[.NET] 利用 async & await 进行异步 IO…
2018-06-17 21:54:44来源:未知 阅读 ()
利用 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 }
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 }
并行异步 I/O
在任务完成后,进入 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,来在中途取消操作。
点我 Demo 下载
同系列的随笔
-
利用 async & await 的异步编程
-
怎样使用 async & await 一步步将同步代码转换为异步编程
-
走进异步编程的世界 - 开始接触 async/await
-
走进异步编程的世界 - 剖析异步方法(上)
-
走进异步编程的世界 - 剖析异步方法(下)
-
走进异步编程的世界 - 在 GUI 中执行异步操作
【参考】https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/concepts/async/using-async-for-file-access
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 如何利用 Shell 脚本来自动监控 Linux 系统的内存? 2019-09-17
- 在无界面centos7上部署MYSQL5.7数据库 2019-07-24
- 利用binlog server及Xtrabackup备份集来恢复误删表 2018-07-29
- 利用binlog server及Xtrabackup备份集来恢复误删表 2018-07-28
- 利用Xtrabackup搭建GTID主从复制(一主一从) 2018-07-25
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash