The server splits asynchronous operation into its two logical parts: the part that takes input from the client and kicks off the asynchronous operation and the part that supplies results of the asynchronous operation to the client. In addition to the input needed for the async operation, the first part also takes an AsyncCallbackDelegate object to be called when the asynchronous operation is completed. The first part returns a waitable object that implements IAsyncResult interface used by the client to determine the status of the asynchronous operation. Server typically also utilizes the waitable object it returned to the client to maintain any state associated with asynchronous operation. The client uses the second part to obtain the results of the asynchronous operation by supplying the waitable object.
The options available to client for initiating asynchronous operations are:
The options available to the client for completing asynchronous operations are:
File IO is an example where both synchronous and asynchronous read/writes to file objects is desirable. An example of how the File object inside classlibs implements read-write operation will server as a good illustration of the design pattern.
class File { // Other methods ……… // Synchronous read unsigned long Read(Byte[] buffer, unsigned long NumToRead); // Asynchrnous read IAsyncResult BeginRead(Byte[] buffer, unsigned long NumToRead, AsyncCallbackDelegate cb); unsigned long EndRead(IAsyncResult ar); // Synchronous read unsigned long Write(Byte[] buffer, unsigned long NumToWrite); // Asynchrnous read IAsyncResult BeginWrite(Byte[] buffer, unsigned long NumToWrite, AsyncCallbackDelegate cb); unsigned long EndWrite(IAsyncResult ar); }
The client cannot easily associate state with a given asynchronous operation without defining a new callback delegate for each operation. This can be fixed by making the begin method take an extra object parameter that represents the state and which is captured into the IAsyncResult.