Welcome

首页 / 软件开发 / C# / C#的异步文件操作

C#的异步文件操作2007-11-10 本站 先来说说同步和异步操作之间的主要区别。在同步I/O操作中,方法将一直处于等待状态,直到I/O操作完成。而在异步I/O操作中,在开始了I/O操作后,程序的方法可以转移去执行其它的操作,这样大大提高了程序执行的效率。

由于是一个多任务的操作系统,在同一时刻系统可能会接受到多个I/O操作请求,要求对磁盘文件执行各种操作。如果采用同步方式,那么每时每刻最多只能有一个I/O操作在进行,而其它的任务都处于等待状态,系统的利用率将会大为降低。异步I/O操作则较好地解决了这种性能上的问题。

Stream类支持在同一个流中既可以进行同步读写,也可以进行异步读写。Stream类是一个抽象类,它为我们提供了BeginRead、BeginWrite、EndReader、EndWrite、Read、Write、Seek等成员方法,协同完成对流的读写操作。所有这些方法都是虚方法。因此,在我们自己设计Stream类的派生类时,我们在类用于读写的成员方法Read和Write中应该重载这些方法,并同时设计它们同步和异步的执行代码。BeginRead,EndRead,BeginWrite和EndWrite方法默认为我们提供的是异步读写操作方式,如果你的派生类的Read和Write方法执行同步操作时,那么程序提供的效率不会很好。只有当它们执行异步操作时,我们才能有效地提高程序的执行效率。

Stream类还提供了ReadByte和WriteByte方法,用于一次读写方式,这时我们就需要编写自己的方法来抛出一个异常。

下面的代码是在.NET联机帮助中提供的一个异步读写操作的例子,程序模拟了一个多处理器系统的工作。

程序清单17-8:

using System;using System.IO;using System.Threading;using BenchUtil;public class BulkImageProcAsync{public const String ImageBaseName="tmpImage-";public const int numImages=200;public const int numPixels=512*512;//ProcessImage has a simple O(N)loop,and we can vary the number//of times we repeat that loop to make the app more CPU-bound or more IO-bound.public static int processImageRepeats=20; //Threads must decrement NumImagesToFinish,and protect //their access to it via a mutex. public static int NumImagesToFinish=numImages; public static Object NumImagesMutex=new Object[0]; //WaitObject is signalled when all image processing is done. public static Object WaitObject=new Object[0]; internal static PerfTimer Pf=new PerfTimer("Asynchronous Bulk Image Processor");public class ImageStateObject{ public byte[] pixels; public int imageNum;}public static void MakeImageFiles(){ int sides=(int)Math.Sqrt(numPixels); Console.Write("Making"+numImages+""+sides+"x"+sides+"images... "); byte[] pixels=new byte[numPixels]; for(int i=0;i<numPixels;i++)pixels[i]=(byte)i; for(int i=0;i<numImages;i++){FileStream fs=new FileStream(ImageBaseName+i+".tmp",FileMode.Create,FileAccess.Write,FileShare.None,8192,false);fs.Write(pixes,0,pixels.Length); FlushFileBuffers(fs.GetHandle());fs.Close(); }Console.WriteLine("Done.");}public static void ReadInImageCallback(IAsyncResult asyncResult){ImageStateObject state=(ImageStateObject)asyncResult.AsyncState;Console.WriteLine("Image"+state.imageNum+"was read."+(asyncResult.CompletedSynchronously?"synchronously":"asyncchronously"));Stream stream=(Stream)asyncResult.AsyncObject;int bytesRead=stream.EndRead(asyncResult);if(bytesRead!=numPixels)throw new Exception("In ReadInImageCallback,got wrong number of bytesfrom the image! got:"+bytesRead);ProcessImages(state.pixels,state.imageNum); stream.Close();//Now write out the image.//using async IO here probably swamps the threadpool,since//there are blocked threadpool threads on soon-to-be-//spawned//threadpool threadsFileStream fs=new FileStream(ImageBaseName+state.imageNum+".done",FileMode.Create,FileAccess.Write,FileShare.None,4096,false);fs.Write(state.pixels,0,numPixels);//IAsyncResult writeResult=fs.BeginWrtie(state.pixels,//0,numPixels,null,null);//fs.EndWrite(writeResult);fs.Close();//Release memory as soon as possible,especially global state.state.pixels=null;//Record that an image is done now.lock(NumImageMutex){ NumImagesToFinish--;if(NumImagesToFinish==0){ Monitor.Enter(WaitObject); Monitor.Pulse(WaitObject); Monitor.Exit(WaitObject); }}}public static void ProcessImage(byte[] pixels,int imageNum){ //Console.WriteLine("ProcessImage"+imageNum); //Do some CPU-intensive operation on the image. for(int i=0;i<processImageReports;i++)for(int j=0;j<numPixels;j++) pixels[j]+=1; //Console.WriteLine("ProcessImage"+imageNum+"done."); }public static void ProcessImagesInBulk(){Console.WriteLine("Processing images...");//int timer=Pf.StartTimer("ProcessImages");int timer=Pf.StartTimer("Total Time");NumImagesToFinish=numImages;AsyncCallback readImagesCallback=new AsyncCallback(ReadInImageCallback);for(int i=0;i<numImages;i++){ImageStateObject state=new ImageStateObject();state.pixels=new byte[numPixels];state.imageNum=i;//Because very large items are read only once,the buffer//on the file stream can be very small to save memory.FileStream fs=new FileStream(ImageBaseName+i+".tmp",FileMode.Open,FileAccess.Read,FileShare.Read,1,true);fs.BeginRead(state.pixels,0,numPixels,readImageCallback,state);} //Ensure all image processing is done.//If not,block until all are finished.bool mustBlock=false; lock(NumImagesMutex) if(NumImagesToFinish>0)mustBlock=true; } if(mustBlock){ Console.WriteLine("All worker threads are queued...Blocking until theycomplete. numLeft:"+NumImagesToFinish); Monitor.Enter(WaitObject);Monitor.Wait(WaitObject); Monitor.Exit(WaitObject); }Pf.StopTimer(timer);Pf.OutputStoppedTime();}public static void Cleanup(){for(int i=0;i<numImages;i++){ File.Delete(ImageBaseName+i+".tmp"); File.Delete(ImageBaseName+i+".done"); }}public static void TryToClearDiskCache(){ //Try to force all pending writes to disk,AND to clear the //disk cache of any data.byte[] bytes=new byte[100*(1<<20)]; for(int i=0;i<bytes.Length;i++)bytes[i]=0; bytes=null;GC.Collect();Thread.Sleep(2000); }public static void Main(String[] args){ Console.WriteLine("Bulk image processing sample application,using asycn IO"); Console.WriteLine("Simulates applying a simple transformation to "+numImages+""imgaes""); Console.WriteLine("ie,Async FileStream & Threadpool benchmark)"); Console.Writeline("Warning - this test requires"+(numPixels*numImages*2)+"bytes of tmp space"); if(args.length==1){processImageRepeats=Int32.Parse(args[0]);Console.WriteLine("ProcessImage inner loop - "+processImageRepeats); } MakeImageFiles(); TryToClearDiskCache(); ProcessImageInBulk(); Cleanup();}[dllimport("KERNEL32",SetlastError=true)]static extern void FlushFileBuffers(int handle);}这里是采用同步方法实现同样功能的程序。程序清单17-9:using System;using System.IO;using System.Threading;using BenchUtil;public class BulkImageProcSync{ public const String ImageBaseName="tmpImage-";public const int numImages=200;public const int numPixels=512*512; //ProcessImage has a simple O(N) loop,and we can vary the number //of times we repeat that loop to make the app more CPU-bound or more IO-bound.public static int processImageRepeats=20;internal static PerfTimer Pf=new PerfTimer("Synchronous Bulk Image Processor");public static void MakeImageFiles(){int sides=(int)Math.Sqrt(numPixels);Console.Write("Making"+numImages+""+sides+"x"+sides+"images...");byte[] pixels=new byte[numPixels];for(int i=0;i<numPixels;i++)pixels[i]=(byte)i;for(int i=0;i<numImages;i++){ FileStream fs=new FileStream(ImageBaseName+i+".tmp",FileMode.Create,FileAccess.Write,FileShare.None,8192,false); fs.Write(pixels,0,pixels.Length); FlushFileBuffers(fs.GetHandle()); fs.Close();}Console.WriteLine("Done.");}public static void ProcessImage(byte[] pixels,int imageNum){//Console.WriteLine("ProcessImage"+imageNum);//Do some CPU-intensive operation on the imagefor(int i=0;i<processImageReports;i++) for(int j=0;j<numPixels;j++)pixels[j]+=1;//Console.WriteLine("processImage"+imageNum+"//done."); }public static void ProcessImagesInBulk(){Console.WriteLine("Processing images...");int timer=Pf.StartTimer("Total Time");byte[] pixels=new byte[numPixels];for(int i=0;i<numImages;i++){ FileStream input=new FileStream(ImageBaseName+i+".tmp",FileMode.Open,FileAccess.Read,FileShare.Read,4196,false); input.Read(pixels,0,numPixels); input:Close();ProcessImage(pixels,i);FileStream output=new FileStream(ImageBaseName+i+".done",FileMode.Create,FileAccess.Write,FileShare.None,4196,false); output.Write(pixels,0,numPixels); output.Close(); } Pf.StopTimer(timer); Pf.OutputStoppedTime(); } public static void Cleanup() {for(int i=0;i<numImages;i++){File.Delete(ImageBaseName+i+".tmp");File.Delete(ImageBaseName+i+".done"); } } public static void TryToClearDiskCache(){ bute[] bytes=new byte[100*(1<<20)]; for(int i=0;i<bytes.Length;i++) bytes[i]=0; bytes=null; GC.Collect(); Thread.Sleep(2000);}public static void Main(String[] args){Console.WriteLine("Bulk image processing sample application,using synchronous IO");Console.WriteLine("Simulates applying a simple transformation to "+numImages+""images"");Console.WriteLine("ie,Sync FileStream benchmark)");Console.WriteLine("Warning - this test requires"+(numPixels*numImages*2)+"bytes of tmp space");if(args.Length==1){ processImageRepeats=Int32.Parse(args[0]); Console.WriteLine("ProcessImage inner loop -"+processImageRepeats);}MakeImageFiles();TryToClearDiskCache(); ProcessImagesInBulk(); Cleanup();}[dllimport"KERNEL32",SetlastError=true)]static extern void FlushFileBuffers(int handlw);}