26.3 异步操作

我们已经知道同步操作和异步操作的区别,假设要向文件中写入较多的数据,在同步I/O操作中,方法将一直处于等待状态,直到写入完成。如果使用异步I/O操作,方法可以在开始了I/O操作后继续运行以执行下面的操作。如果使用回调方法,I/O操作执行完毕后会调用回调方法以通知主线程执行完毕。

我们以FileStream为例阐述文件操作中的异步操作。FileStream类有15个构造函数,只有1个构造函数可以指定使用异步I/O,如下:


public FileStream(string path,FileMode mode,FileAccess access,FileShare share,int bufferSize,bool useAsync);


最后一个参数useAsync用于指定是使用异步I/O还是同步I/O,如果应用程序打算利用异步I/O,将useAsync参数设置为true。注意,有些操作系统有可能不支持异步I/O,即使设置为true,也可能使用的是同步I/O。

当异步打开时,BeginRead和BeginWrite方法在执行大量读和写操作时效果更好,但对于少量的读和写操作时,这些方法的速度可能要慢得多。正确使用异步I/O,可以使应用程序的速度加快10倍,但是如果在没有为异步I/O重新设计应用程序的情况下使用异步I/O,则可能使性能降低10倍。

我们使用异步I/O的方式来改写之前的例子,即改写代码清单26-3。首先,使用异步方式初始化FileStream对象,然后调用FileStream的BeginWrite方法。这里使用的是回调方法,因此提供了一个回调方法WriteCompleted。在回调方法中,调用FileStream基类Stream的EndWrite方法等待异步写入完成,在运行结果中注意观察主线程和异步写入线程的线程ID,如代码清单26-6所示。

代码清单26-6 异步写入文件示例


using System;

using System.IO;

using System.Text;

using System.Threading;

namespace ProgrammingCSharp4

{

class IOSample

{

public static void Main()

{

//声明文件流对象

FileStream fs=null;//获得当前主线程ID

int threadId=Thread.CurrentThread.ManagedThreadId;Console.WriteLine(“当前主线程:id[{0}]”,threadId);

try

{

//操作的目录

string dirPath=@"c:\iosample";

string filePath=string.Format(@"{0}{1}",dirPath,"mytext.txt");

DirectoryInfo di=new DirectoryInfo(dirPath);if(!di.Exists)

{

di.Create();

Console.WriteLine(@“创建一个目录:c:\iosample”);}

else

{

Console.WriteLine(@“目录:c:\iosample已存在”);}

Console.WriteLine(“目录名:{0}”,di.Name);

FileInfo fi=new FileInfo(filePath);if(!fi.Exists)

{

File.Create(filePath);

Console.WriteLine(“新建一个文件:{0}”,filePath);}

//最后一个参数为true表示使用异步方式操作文件流

fs=new FileStream(filePath,FileMode.Append,FileAccess.Write,FileShare.None,4096,true);

Console.WriteLine(“文件:{0}已存在,打开它”,filePath);

string newContent=DateTime.Now.ToString()+"\n";byte[]buffer=Encoding.UTF8.GetBytes(newContent);//指定WriteCompleted为回调函数

AsyncCallback callback=WriteCompleted;//开始异步操作

IAsyncResult result=fs.BeginWrite(buffer,0,buffer.Length,callback,fs);Console.WriteLine("-".PadRight(60,'-'));

Console.WriteLine(“正在异步写入,请稍候……”);Console.ReadKey();

}

finally{

fs.Close();

Console.WriteLine(“关闭数据流……”);}

}

//异步完成后的回调函数

public static void WriteCompleted(IAsyncResult asyncResult){

if(null==asyncResult){

throw new ArgumentException();}

//获得异步执行时所在的线程ID

int threadId=Thread.CurrentThread.ManagedThreadId;Console.WriteLine(“异步写入开始……”);

Console.WriteLine(“异步写入操作位于线程:id[{0}]”,threadId);//asyncResult.AsyncState实际上是FileStream类型

Stream stream=(Stream)asyncResult.AsyncState;stream.EndWrite(asyncResult);

stream.Close();

Console.WriteLine(“异步写入完毕……”);

Console.WriteLine("-".PadRight(60,'-'));}

}}


运行结果如下:


当前主线程:id[1]

目录:c:\iosample已存在目录名:iosample

文件:c:\iosample\mytext.txt已存在,打开它


正在异步写入,请稍候……

异步写入开始……

异步写入操作位于线程:id[4]异步写入完毕……


关闭数据流……

请按任意键继续……