■
4.4 Asynchronous I/O 117
4.4 Asynchronous I/O
The .NET framework provides a number of predefined network class methods that exe-
cute asynchronously. This allows code execution in the calling code to proceed while
the I/O method waits to unblock. What’s actually happening is that the asynchronous
method is being executed in its own thread, except the details of setting up, data
passing, and starting the thread are done for you. The calling code has three options
to determine when the I/O call is completed: (1) it can specify a callback method to
be invoked on completion; (2) it can poll periodically to see if the method has com-
pleted; or (3) after it has completed its asynchronous tasks, it can block waiting for
completion.
The .NET framework is extremely flexible in how it provides asynchronous API
capabilities. First, its library classes provide predefined nonblocking versions of meth-
ods for many different types of I/O, not just network calls. There are nonblocking
versions of calls for network I/O, stream I/O, file I/O, even DNS lookups. Second,
the .NET framework provides a mechanism for building an asynchronous version of
any method, even user-defined methods. The latter is beyond the scope of this
book, but in this section we will examine some of the existing asynchronous network
methods.
An asynchronous I/O call is broken up into a begin call that is used to initiate
the operation, and an end call that is used to retrieve the results of the call after it has
completed. The begin call uses the same method name as the blocking version with the
word Begin prepended to it. Likewise, the end call uses the same method name as the
blocking version with the word End prepended to it. Begin and end operations are intended
to be symmetrical, and each call to a begin method should be matched (at some point)
with an end method call. Failure to do so in a long-running program creates an accumu-
lation of state maintenance for the uncompleted asynchronous calls... in other words, a
memory leak!
Let’s look at some concrete examples. The NetworkStream class contains asyn-
chronous versions of its Write() and Read() methods, implemented as BeginWrite(),
EndWrite(), BeginRead(), and EndRead(). Let’s take a look at these methods and examine
how they relate to their blocking counterparts.
The BeginRead() and BeginWrite() methods take two additional arguments and have
a different return type:
public override IAsyncResult BeginRead(byte[] buffer, int offset, int size,
AsyncCallback callback, object state);
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
AsyncCallback callback, object state);
The two additional arguments are an instance of AsyncCallback and an instance of
object, which can be any C# class instance (predefined or user-defined). The AsyncCall-
back class is a delegate that specifies the callback method to invoke when the asynchronous
option is complete. This class can be instantiated simply by passing it the name of the