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]异步写入完毕……
关闭数据流……
请按任意键继续……