利用 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. ExtJS 4.2 评分组件

    上一文章是扩展ExtJS自带的Date组件.在这里将创建一个评分组件. 目录 1. 介绍 2. 示例 3. 资源下载 1. 介绍 代码参考的是 Sencha Touch 2上的一个RatingStar ...

  2. HTTP协议系列(1)

    一.为什么学习Http协议       首先明白我们为什么学习HTTP协议,也就是说明白HTTP协议的作用.HTTP协议是用于客户端与服务器之间的通讯.明白了HTTP协议的作用也就知道了为什么要学习H ...

  3. 在离线环境中发布.NET Core至Windows Server 2008

    在离线环境中发布.NET Core至Windows Server 2008 0x00 写在开始 之前一篇博客中写了在离线环境中使用.NET Core,之后一边学习一边写了一些页面作为测试,现在打算发布 ...

  4. 说说Golang的使用心得

    13年上半年接触了Golang,对Golang十分喜爱.现在是2015年,离春节还有几天,从开始学习到现在的一年半时间里,前前后后也用Golang写了些代码,其中包括业余时间的,也有产品项目中的.一直 ...

  5. hadoop2.7之Mapper/reducer源码分析

    一切从示例程序开始: 示例程序 Hadoop2.7 提供的示例程序WordCount.java package org.apache.hadoop.examples; import java.io.I ...

  6. Spring(三)__aop编程

    aop( aspect oriented programming ) 面向切面编程,是对所有对象或者是一类对象编程 几个重要的概念: 1.切面(aspect):要实现的交叉功能,是系统模块化的一个切面 ...

  7. [DJANGO] excel十几万行数据快速导入数据库研究

    先贴原来的导入数据代码: 8 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "www.setting ...

  8. 《月之猎人 (Moon Hunters)》主角设计

    原文链接 游戏开发人员,你们好! 我是 Kitfox Games 工作室的总监 Tanya,我们的工作室位于加拿大的蒙特利尔,拥有六名员工. 我们 3 月份发布了<月之猎人>游戏的桌面版, ...

  9. 如何开发一个Jquery插件

    Jquery有两种开发插件的方法: 1.jquery.fn.extend(object); 2.jquery.extend(object); 第一种方法是给Jquery对象添加方法,jquery.fn ...

  10. VS2012+EF6+Mysql配置心路历程

    为了学习ORM,选择了EntityFramework,经历了三天两夜的煎熬,N多次错误,在群里高手的帮助下,终于成功,现在将我的心路历程记录下来,一是让自己有个记录,另外就是让其它人少走些弯路. 我的 ...