先來說說同步和異步操作之間的主要區別。在同步I/O操作中,方法將一直處於等待狀態,直到I/O操作完成。而在異步I/O操作中,在開始了I/O操作後,程序的方法可以轉移去執行其它的操作,這樣大大提高了程序執行的效率。
由於Windows是一個多任務的操作系統,在同一時刻系統可能會接受到多個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 bytes
from 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 threads
FileStream 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 they
complete. 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 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("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);
}