Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Asynchronous Calls in .NET : Page 5

Invoking methods asynchronously involves a substantial amount of infrastructure: keeping track of methods in progress, multithreading, and error handling. Fortunately, .NET provides a standard method for asynchronous invocation that's consistent and available to every component and client.


advertisement

Performing Asynchronous Operations without Delegates
Delegate-based asynchronous calls like those described in the preceding sections let you asynchronously invoke any method on any class. This provides valuable flexibility to a client, but requires that you define a delegate with a signature that matches the method you want to invoke. Sometimes, certain operations such as disk or network access, Web requests, Web service calls, or message queuing are long in duration or even open-ended in their very nature. In such cases, you will usually opt to invoke the operations asynchronously. The designers of the .NET Framework wanted to ease the task of performing such operations asynchronously by building methods into the classes that offer them Begin and End. These methods always take a form very similar to the BeginInvoke() and EndInvoke() methods provided by a delegate class:

   public <return type> <Operation>(<parameters>);
   IAsyncResult Begin<Operation>(
              <input and input/output parameters>,
              AsyncCallback callback,
              object asyncState); 
   public <return type> End<Operation>(
              <output and input/output parameters> 
              IAsyncResult asyncResult);
For example, the abstract Stream class, defined in the System.IO namespace, provides asynchronous Read() and Write() operations:

   public class Stream : MarshalByRefObject,
                         IDisposable
   {
      public virtual int Read(byte[]buffer,
                              int offset,int count);
      public virtual IAsyncResult BeginRead(
                              byte[]buffer,
                              int offset,
                              int count,
                              AsyncCallback callback,
                              object state);
      public virtual int EndRead(
                           IAsyncResult asyncResult);
      public virtual void Write(byte[]buffer,
                              int offset,int count);
      public virtual IAsyncResult BeginWrite(
                              byte[]buffer,
                              int offset,
                              int count,
                              AsyncCallback callback,
                              object state);
      public virtual void EndWrite(
                           IAsyncResult asyncResult);
      /* Other methods and properties */
   }
The Stream class is the base class for all other stream classes, such as FileStream, MemoryStream, and NetworkStream. All the Stream-derived classes override these methods and provide their own implementation.

Another example of a class that provides its own asynchronous methods is the Web service wrapper class that Visual Studio .NET automatically adds to a client project when the client adds a reference to a Web service. Imagine that the Calculator class in the following code snippet exposes its methods, such as Add(), as Web services:



   using System.Web.Services;
   public class Calculator
   {
      [WebMethod]
      public int Add(int num1,int num2)
      {
         return = num1+num2;
      }
      //Other methods
   }
The wrapper class auto-generated for the client by Visual Studio .NET will contain BeginAdd() and EndAdd() methods used to invoke the Web service asynchronously:

   using System.Web.Services.Protocols;
   public class Calculator : SoapHttpClientProtocol
   {
      public int Add(int num1,int num2) 
      {...}
      public IAsyncResult BeginAdd(int num1,int num2,
         AsyncCallback callback,object asyncState) 
      {...}
      public int EndAdd(IAsyncResult asyncResult) 
      {...}
      /* Other methods  */
   }
Using non-delegate-based asynchronous method calls is similar to using the BeginInvoke() and EndInvoke() methods provided by a delegate class: you dispatch the asynchronous operation using Begin and can either call End to block until completion, wait for the operation (or multiple operations) to complete, or use a callback method. However, there is no uniform requirement to call End on the original object that you used to dispatch the Begin call. With some classes (like Web service wrapper classes or Stream-derived classes), you can create a new object and call End on it. Listing 9 demonstrates this technique when using a Web service wrapper class.

Listing 10 demonstrates asynchronous read operation on a FileStream object. Note that you pass the useAsync parameter to the FileStream constructor, indicating asynchronous operations on the stream.

Asynchronous Error Handling
Output parameters and returned values are not the only thing unavailable at the time an asynchronous call is dispatched—exceptions are missing as well. After calling BeginInvoke(), control returns to the client, but it may be some time until the asynchronous method encounters an error and throws an exception, and it may be some time after that until the client actually calls EndInvoke(). .NET must therefore provide for some way for the client to know that an exception was thrown and for the client to handle it. The .NET solution is straightforward: When the asynchronous method throws an exception, .NET catches that exception. When the client calls EndInvoke(), .NET re-throws that exception object, letting the client handle the exception. If a callback method is provided, .NET calls the callback method immediately after the exception is thrown on the object side. For example, suppose the Calculator class has a Divide() method, defined as:

   public class Calculator
   {
      public int Divide(int num1,int num2)
      {
         return = num1/num2;
      }
      //Other methods
   }  
Divide() will throw a DivideByZeroException if the denominator (num2) passed in is zero. Listing 11 demonstrates how you might handle this error.

By providing you with asynchronous calls support, .NET lets you focus on the domain problems at hand rather than on complicated asynchronous plumbing. You can use delegate-based asynchronous invocation on any type, be it .NET or your own, and once you have mastered it, asynchronous calls are consistent across all cases, projects, and teams. You can even build-in such support into your own types if you anticipate the need for asynchronous calls (like the Stream class does), and use delegates internally to implement the asynchronous call invocation.



Juval Löwy is a software architect and the principal of IDesign, a consulting and training company focused on .NET design and .NET migration. This article is derived from his latest book, "Programming .NET Components" (O'Reilly, 2003). Juval is Microsoft's Regional Director for the Silicon Valley, helping the industry adopt .NET. He is also a frequent speaker at the major international software development conferences. Contact him at www.idesign.net.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date