利用 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 语句方法的定义。

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

btnWrite_Click

         /// <summary>
/// 异步写入文件
/// </summary>
/// <returns></returns>
private async Task WriteTextAsync()
{
var path = $"temp.txt";
var content = Guid.NewGuid().ToString(); using (var fs = new FileStream(path,
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize: , useAsync: true))
{
var buffer = Encoding.UTF8.GetBytes(content); //var writeTask = fs.WriteAsync(buffer, 0, buffer.Length);
//await writeTask;
await fs.WriteAsync(buffer, , buffer.Length);
}
}

  行号 17 的语句可以修改为:

   //await fs.WriteAsync(buffer, 0, buffer.Length);
//可以改为
var writeTask = fs.WriteAsync(buffer, , buffer.Length);
await writeTask;

  第一个语句(行号 1)返回任务并导致进程的文件。使用等待的第二个语句(行号3、4)导致方法立即退出并返回其他任务。当随后处理的文件完成时,执行回 await 的语句。

异步读取文本

  下面的示例是从文件中读取文本。将文本缓冲区的内容放入 StringBuilder。不同于在前面的示例,会不断 await 一个读取的长度值。ReadAsync 方法返回 Task<Int32>,即 Task<int>,因此,等待的计算生成一个 Int32 值(numRead),在操作完成之后。

         /// <summary>
/// 异步读取文本
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private async Task<string> ReadTextAsync(string fileName)
{
using (var fs = new FileStream(fileName,
FileMode.OpenOrCreate, FileAccess.Read, FileShare.None, bufferSize: , useAsync: true))
{
var sb = new StringBuilder();
var buffer = new byte[0x1000]; //十六进制 等于十进制的 4096
var readLength = ; while ((readLength = await fs.ReadAsync(buffer, , buffer.Length)) != )
{
var text = Encoding.UTF8.GetString(buffer, , readLength);
sb.Append(text);
} return sb.ToString();
}
}
         private async void btnRead_Click(object sender, RoutedEventArgs e)
{
var fileName = $"temp.txt";
if (!File.Exists(fileName))
{
Debug.WriteLine($"文件找不到:{fileName}");
return;
} try
{
var content = await ReadTextAsync(fileName);
Debug.WriteLine(content);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}

btnRead_Click

并行异步 I/O

  下面的示例通过编写多个文本文件进行演示并行处理。对于每个文件,WriteAsync 方法返回后将被添加到任务列表的集合中。在处理完成所有的任务时,await Task.WhenAll(tasks); 语句将退出方法并恢复执行。

  在任务完成后,进入 finally 块的将所有 FileStream 实例进行清理回收。如果直接在 using 语句中创建 FileStream 实例,则 FileStream 实例可能在任务完成之前就被处理。

  【注意】所有性能提高几乎完全是异步并行处理。异步的优点是它不会占用多个线程,也就是说,它不会占用用户界面线程。

         /// <summary>
/// 异步写入多个文件
/// </summary>
/// <param name="folder"></param>
/// <returns></returns>
private async Task WriteMultiTextAsync(string folder)
{
var tasks = new List<Task>();
var fileStreams = new List<FileStream>(); try
{
for (int i = ; i <= ; i++)
{
var fileName = Path.Combine(folder, $"{i}.txt");
var content = Guid.NewGuid().ToString();
var buffer = Encoding.UTF8.GetBytes(content); var fs = new FileStream(fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize: , useAsync: true);
fileStreams.Add(fs); var writeTask = fs.WriteAsync(buffer, , buffer.Length);
tasks.Add(writeTask);
} await Task.WhenAll(tasks);
}
finally
{
foreach (var fs in fileStreams)
{
fs.Close();
fs.Dispose();
}
}
}
         private async void btnWriteMulti_Click(object sender, RoutedEventArgs e)
{
var folder = $"temp"; if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
} await WriteMultiTextAsync(folder);
}

btnWriteMulti_Click

  在使用 WriteAsync 和 ReadAsync 方案时,你可以指定 CancellationToken,来在中途取消操作。

同系列的随笔


 【参考】https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/concepts/async/using-async-for-file-access

[.NET] 利用 async & await 进行异步 IO 操作的更多相关文章

  1. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  2. 利用 async & await 的异步编程

    走进异步编程的世界 - 开始接触 async/await 利用 async & await 的异步编程 async 的三大返回类型 公司技术需求备忘录

  3. 深入理解协程(三):async/await实现异步协程

    原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的最后一篇. 从本篇你将了解到: ...

  4. Atitit. Async await 优缺点 异步编程的原理and实现 java c# php

    Atitit. Async await 优缺点 异步编程的原理and实现 java c# php 1. async & await的来源1 2. 异步编程history1 2.1. 线程池 2 ...

  5. 使用ES6新特性async await进行异步处理

    我们往往在项目中会遇到这样的业务需求,就是首先先进行一个ajax请求,然后再进行下一个ajax请求,而下一个请求需要使用上一个请求得到的数据,请求少了还好说,如果多了,就要一层一层的嵌套,就好像有点c ...

  6. node.js零基础详细教程(4):node.js事件机制、node异步IO操作

    第四章 建议学习时间3小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  7. async+await处理异步问题

    在编写网页的时候我们常常会遇到异步问题,async+await是es6提出的解决异步的方法,下面我们来看看这个方法怎么实现解决异步的, 大家都知道,setTimeout是一个定时器.他是一个异步执行的 ...

  8. NodeJs通过async/await处理异步

    ##场景 远古时代 我们在编写express后台,经常要有许多异步IO的处理.在远古时代,我们都是用chunk函数处理,也就是我们最熟悉的那种默认第一个参数是error的函数.我们来模拟一个Mongo ...

  9. js async await 终极异步解决方案

    既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Pr ...

随机推荐

  1. Fis3的前端工程化之路[三大特性篇之声明依赖]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  2. TODO:小程序开发过程之体验者

    TODO:小程序开发过程之体验者 1. 小程序开发过程,先下载开发者并安装开发者工具,现在腾讯开放测试了,普通用户也可以登录开发者工具,如图普通用户登录为调试类型,但是只能建立无AppID的项目 如果 ...

  3. .NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?

    配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个 ...

  4. 如何在网页中提取Email地址

    开博好久了,今天第一次发表技术文档,之前总是将一些好的事例保存在电脑,时间久了找起来也很麻烦,所以还是放在博客里进行归类比较方便,这样也能将自己在学习过程中的一些心得体会分享给大家,也能给需要的人一点 ...

  5. 编写高质量代码:改善Java程序的151个建议(第6章:枚举和注解___建议88~92)

    建议88:用枚举实现工厂方法模式更简洁 工厂方法模式(Factory Method Pattern)是" 创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其它子类" ...

  6. 微软发布VSBT,无需安装Visual Studio即可实现项目编译

    安装了Visual Studio的那些使用微软平台的开发者通常能够非常容易地操作自己的项目:打开解决方案,修改内容,设置好所有必须的文件以及配置后编译项目.但是在构建服务器或者持续交付系统等没有安装V ...

  7. Linux系统中的Device Mapper学习

    在linux系统中你使用一些命令时(例如nmon.iostat 如下截图所示),有可能会看到一些名字为dm-xx的设备,那么这些设备到底是什么设备呢,跟磁盘有什么关系呢?以前不了解的时候,我也很纳闷. ...

  8. 《Note --- Unreal 4 --- Sample analyze --- StrategyGame(continue...)》

    ---------------------------------------------------------------------------------------------------- ...

  9. 深入理解Spring MVC

    如何让一个普通类成为Controller? 方案一:实现接口Controller 解析:handleRequest(request,response) 方案二:继承AbstractController ...

  10. Nodejs之MEAN栈开发(六)---- 用Angular创建单页应用(上)

    在上一节中我们学会了如何在页面中添加一个组件以及一些基本的Angular知识,而这一节将用Angular来创建一个单页应用(SPA).这意味着,取代我们之前用Express在服务端运行整个网站逻辑的方 ...