Asynchronous Delegates

One of the basic building blocks in .NET is the delegate. Delegates are often described as type-safe function pointers. They are function pointers because they enable you to store a reference that "points" to a class method. They are type-safe because every delegate is defined with a fixed method signature. For example, you might create a delegate variable that can point to any method that takes two string parameters and returns an integer value. This is the delegate signature. You can't use this delegate to store a reference to a method with a different signature (for example, one that takes two string parameters and has no return value).

The two core delegate classes are as follows:

  • System.Delegate

    The base delegate type, which can point to a single method. This is usually the type of delegate you'll use when you create a delegate on your own. This is also the type of delegate that this chapter explores.

  • System.MulticastDelegate

    Represents a delegate that can point to more than one method at a time. This delegate is the basis for .NET event handling.

Suppose, for example, that you have a project that contains the following object:

 Public Class ProductList     Public Function GetTotal() As Decimal         ' (Code omitted.)     End Function End Class 

To create a delegate that points to GetTotal, you need to define the function signature. We'll call this GetTotalDelegate. Typically, you define this delegate inside the class consumer.

 Public Delegate Function GetTotalDelegate() As Decimal 

Now you can create a delegate variable based on GetTotalDelegate that can point to any function that accepts no parameters and returns a decimal result. Listing 6-1 is a code example that creates a ProductList object and a GetTotalDelegate that points to the appropriate GetTotal method.

Listing 6-1 Invoking a method through a delegate
  ' Declare the object. Dim List As New ProductList() ' Declare the delegate variable. Dim GetTotalPointer As New GetTotalDelegate(AddressOf List.GetTotal) ' Call List.GetTotal() through the delegate. ' This is the line that invokes the delegate. Dim Total As Decimal Total = GetTotalPointer() 

So far, this is only an example of .NET basics. However, delegates are more interesting under the hood because they include built-in support for asynchronous execution.

Essentially, it works like this: Every time you define a delegate, .NET generates a custom delegate class. When you create a delegate and "point" it to a method, the custom delegate object stores a reference to the appropriate method. When you invoke the delegate, you are actually using the Invoke method in the custom delegate class.

The Invoke method provides synchronous execution, but the delegate class also includes two other methods BeginInvoke and EndInvoke that enable you to call the referenced method asynchronously on another thread. These asynchronous methods don't work the same as the basic Invoke method. BeginInvoke returns immediately, allowing your code to continue its work. BeginInvoke also returns a special IAsyncResult object that you can examine to determine when the asynchronous operation is complete. To pick up the results at a later time, you submit this IAsyncResult object to the EndInvoke method, which waits for the operation to complete if it hasn't already finished and returns the information in which you're interested.

Listing 6-2 shows the same code rewritten to use asynchronous delegates.

Listing 6-2 Invoking a method asynchronously through a delegate
  ' Declare the object. Dim List As New ProductList() ' Declare the delegate variable. Dim GetTotalPointer As New GetTotalDelegate(AddressOf List.GetTotal) ' Call List.GetTotal() through BeginInvoke(). ' Note that BeginInvoke always takes two extra parameters, and ' returns immediately. Dim AsyncResult As IAsyncResult AsyncResult = GetTotalPointer.BeginInvoke(Nothing, Nothing) ' (Perform some other work while the GetTotal() method is executing.) ' Retrieve the final result using the IAsyncResult. Dim Total As Decimal = GetTotalPointer.EndInvoke(AsyncResult) 

Figure 6-3 diagrams how this code works.

Figure 6-3. Calling a method asynchronously with a delegate

graphics/f06dp03.jpg

If the GetTotal method hasn't finished by the time you call EndInvoke, your code just waits for it to finish. In some cases, you might want to know whether the method is actually complete (and ready for pickup) before you call EndInvoke so that you won't risk a long wait. You can make this determination by examining the IsCompleted property of the IAsyncResult object, as shown in Listing 6-3. Checking this information repeatedly (for example, in a loop) is known as polling.

Part of the reason this delegate approach is so straightforward is because the asynchronous method doesn't have any other dependencies. It doesn't need to communicate its result or read information from another object. Thanks to this independence, you won't need to worry about synchronization or concurrency problems.

Listing 6-3 Polling to determine when an asynchronous method is complete
 Dim List As New ProductList() Dim GetTotalPointer As New GetTotalDelegate(AddressOf List.GetTotal) ' Call List.GetTotal() through BeginInvoke(). Dim AsyncResult As IAsyncResult AsyncResult = GetTotalPointer.BeginInvoke(Nothing, Nothing) ' Loop until the method is complete. Do Until AsyncResult.IsCompleted     ' (Perform some additional work here.) Loop ' Retrieve the final result. Dim Total As Decimal = GetTotalPointer.EndInvoke(AsyncResult) 

Table 6-1 shows the full set of IAsyncResult properties. Note that polling is rarely the most efficient approach. Generally, it's a better idea to use a callback or a wait handle. We'll look at these options parameters later in this chapter.

Table 6-1. IAsyncResult Properties

Property

Description

AsyncState

Returns the object that was supplied as the last parameter of the BeginInvoke call. This is useful when you're making multiple asynchronous calls at once because it enables you to associate an object with each call.

AsyncWaitHandle

Returns a System.Threading.WaitHandle object, which is primarily useful if you want to wait for a batch of asynchronous calls to complete.

CompletedSynchronously

Returns True if the method actually completed on the caller's thread. Typically, this will always be False. One possible exception is with asynchronous file I/O calls, where the work might be performed in the caller's thread if it is small (less than 64 KB).

IsCompleted

Returns True when the underlying method has completed. Otherwise, it returns False.

Note

You won't find Invoke, BeginInvoke, and EndInvoke in the class library reference for the System.Delegate type because these methods aren't a part of the basic delegate types. They are methods that .NET creates automatically when you define a delegate. (Technically, they are part of the custom delegate class that .NET builds behind the scenes.) If you think about it, you'll realize that this is a necessary fact of life because the BeginInvoke and EndInvoke have different signatures depending on the underlying method they are calling. For example, BeginInvoke always takes the parameters of the original method, along with two extra parameters, and EndInvoke always uses the return value of the original method. Thus, there's no way that they can be defined generically.


Asynchronous Remoting Calls

With .NET Remoting, the process is exactly the same. You can create a delegate variable that points to the method of a remote object and call it by using BeginInvoke. Technically, you aren't calling the remote object asynchronously you're calling the transparent proxy asynchronously. (Remember, the transparent proxy is created automatically by the .NET Remoting infrastructure to manage communication between components in different application domains.) The proxy waits for the response from the remote object on a separate thread, as shown in Figure 6-4.

Figure 6-4. An asynchronous call to a remote component

graphics/f06dp04.jpg

Asynchronous Web Service Calls

It is possible to use delegates in much the same way with XML Web services, by creating a delegate variable that points to a proxy class method and calling this delegate asynchronously. However, you don't need to go to this extra work. When .NET generates a proxy class, it automatically adds additional methods that allow the XML Web service to be called asynchronously.

Suppose, for example, that you have the following Web method:

 <WebMethod> _ Public Function GetStockQuote(ByVal Ticker As String) As Decimal     ' (Code omitted.) End Function 

The proxy class will include a corresponding GetStockQuote method that calls this method synchronously. However, the proxy class will also include BeginGetStockQuote and EndGetStockQuote methods that call the Web service asynchronously. Listing 6-4 shows these three methods in the proxy class.

Listing 6-4 Asynchronous support in the Web service proxy class
 Public Function GetStockQuote(ByVal Ticker As String) As Decimal     Dim results() As Object = Me.Invoke("GetStockQuote", _                               New Object() {Ticker})     Return CType(results(0), Decimal) End Function          Public Function BeginGetStockQuote(ByVal Ticker As String, _  ByVal callback As AsyncCallback, ByVal asyncState As Object) _  As IAsyncResult     Return Me.BeginInvoke("GetStockQuote", _            New Object() {Ticker}, callback, asyncState) End Function          Public Function EndGetStockQuote(ByVal asyncResult As IAsyncResult) _  As Decimal     Dim results() As Object = Me.EndInvoke(asyncResult)     Return CType(results(0), Decimal) End Function 

These methods mirror the BeginInvoke and EndInvoke methods. BeginStockQuote accepts the original string parameter, plus two optional ones, and returns an IAsyncResult instance. EndGetStockQuote accepts the IAsyncResult object and returns the GetStockQuote decimal return value.

To provide this magic, you'll notice that the proxy class code calls its own BeginInvoke method. This is actually one of the methods inherited from the base class SoapHttpClientProtocol (found in the System.Web.Services.Protocols namespace). All proxy classes that use SOAP communication derive from this class, and so they all have the ability to call Web methods asynchronously.



Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

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