Asynchronous IO Pattern


Asynchronous I/O Pattern

Using the asynchronous I/O pattern whenever possible provides what is probably the single greatest performance boost. Asynchronous I/O is the only method that can efficiently manage hundreds or thousands of simultaneous operations on multiple resources. The only alternative is creating multiple threads and issuing blocking calls ”but this solution doesn t scale well. Threads are an expensive resource and there is a cost associated with every context switch from one thread to another.

For example, consider a TCP server implemented using the Socket class. If the server is to handle 1000 client connections simultaneously , how will it do so? Many developers would create a thread to handle communication on each connection, but a process that spawns 1000 threads will quickly exhaust available resources and spend needless CPU cycles switching execution between the threads.

The more efficient method is to use the asynchronous I/O pattern, which typically utilizes the .NET Framework thread pool to handle requests . An application posts one or more asynchronous operations and specifies the delegate methods , which are invoked upon completion. These delegates are called from the thread pool maintained by the Framework. This thread pool is a constrained resource and it is important that it not spend too much time in the asynchronous completion routine. That is, the delegate should simply call the end routine for the initiated call (for example, calling EndReceive in response to the BeginReceive ) and then return. If the application needs to perform any kind of processing upon completion, the completion routine should not compute in the completion routine ”it should queue the operation to be handled by an application-spawned thread.

The following code sample illustrates this process. The IoPacket class is the context information associated with each asynchronous operation. In the sample, IoPacket contains the byte buffer that received the data, the TCP Socket object, and the number of bytes actually received into the buffer. The HandleReceive function is the asynchronous callback invoked when the receive completes. If the receive succeeded by receiving data, the IoPacket object is added to a list and an event is signaled. The ReceiveThread method is a spawned thread that waits for the event to be signaled and then walks the list of pending IoPacket objects and processes them. Note that access to the list is synchronized by using the Monitor class.

C#

 ArrayListreceiveList=newArrayList(); ManualResetEventreceiveEvent=newManualResetEvent(false); publicclassIoPacket { publicbyte[]receiveBuffer=newbyte[4096]; publicSockettcpSocket; publicintbytesRead; //Otherstateinformation } voidHandleReceive(IAsyncResultar) { IoPacketioData=(IoPacket)ar.AsyncState; ioData.bytesRead=ioData.tcpSocket.EndReceive(ar); if(ioData.bytesRead==0) { //Connectionhasclosed ioData.tcpSocket.Close(); } else { Monitor.Enter(receiveList); receiveList.Add(ioData); receiveEvent.Set(); Monitor.Exit(receiveList); ioData=newIoPacket(); //PostanotherBeginReceivewiththenewioDataobject } } voidReceiveThread() { IoPacketioData; boolrc; while(true) { rc=receiveEvent.WaitOne(); if(rc==true) { receiveEvent.Reset(); Monitor.Enter(receiveList); while(receiveList.Count>0) { ioData=(IoPacket)receiveList[0]; receiveList.RemoveAt(0); //Dosomethingwithdata } Monitor.Exit(receiveList); } } } 

Visual Basic .NET

 DimreceiveListAsArrayList=NewArrayList DimreceiveEventAsManualResetEvent=NewManualResetEvent(False) PublicClassIoPacket PublicreceiveBuffer(4096)AsByte PublictcpSocketAsSocket PublicbytesReadAsInteger Otherstateinformation EndClass PublicSubHandleReceive(ByValarAsIAsyncResult) DimioDataAsIoPacket=ar.AsyncState ioData.bytesRead=ioData.tcpSocket.EndReceive(ar) If(ioData.bytesRead=0)Then Connectionhasclosed ioData.tcpSocket.Close() Else Monitor.Enter(receiveList) receiveList.Add(ioData) receiveEvent.Set() Monitor.Exit(receiveList) ioData=NewIoPacket PostanotherBeginReceivewiththenewioDataobject EndIf EndSub PublicSubReceiveThread() DimioDataAsIoPacket DimrcAsBoolean While(True) rc=receiveEvent.WaitOne() If(rc=True)Then receiveEvent.Reset() Monitor.Enter(receiveList) While(receiveList.Count>0) ioData=receiveList(0) receiveList.RemoveAt(0) Dosomethingwithdata EndWhile Monitor.Exit(receiveList) EndIf EndWhile EndSub 

Consider an application that creates multiple connections to a server and receives data that must be written to local files. The application posts one or more asynchronous receive operations on each connection. When the completion routines fire, the application takes the receive buffer, which now contains data, and enqueues the data in some kind of array (such as an ArrayList ). An event is then signaled to wake up an application thread to dequeue the buffer and then write the data to disk. The application avoids performing blocking operations in the asynchronous delegate. This prevents the Framework s thread pool from being blocked and it also disassociates the network operations from the disk operations, which allows data to be received at the fastest rate possible.

Note  

In versions 1.0 and 1.1 of the .NET Framework, the necessary code access security checks create considerable overhead in executing a callback function when an asynchronous method completes. This overhead results in significantly decreased performance when compared to native Winsock applications. Significant performance improvements for asynchronous callbacks are anticipated in the next major release of the .NET Framework.




Network Programming for the Microsoft. NET Framework
Network Programming for the MicrosoftВ® .NET Framework (Pro-Developer)
ISBN: 073561959X
EAN: 2147483647
Year: 2003
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net