Thread in C# - Part 13: Asynchronous Delegates  
 

(Post 20/11/2007) In Part 1 we described how to pass data to a thread, using ParameterizedThreadStart. Sometimes you need to go the other way, and get return values back from a thread when it finishes executing. Asynchronous delegates offer a convenient mechanism for this, allowing any number of typed arguments to be passed in both directions. Furthermore, unhandled exceptions on asynchronous delegates are conveniently re-thrown on the original thread, and so don't need explicit handling. Asynchronous delegates also provide another way into the thread pool.

The price you must pay for all this is in following its asynchronous model. To see what this means, we'll first discuss the more usual, synchronous, model of programming. Let's say we want to compare two web pages. We could achieve this by downloading each page in sequence, then comparing their output as follows:

static void ComparePages() {
WebClient wc = new WebClient ();
string s1 = wc.DownloadString ("http://www.oreilly.com");
string s2 = wc.DownloadString ("http://oreilly.com");
Console.WriteLine (s1 == s2 ? "Same" : "Different");
}

Of course it would be faster if both pages downloaded at once. One way to view the problem is to blame DownloadString for blocking the calling method while the page is downloading. It would be nice if we could call DownloadString in a non-blocking asynchronous fashion, in other words:

  1. We tell DownloadString to start executing.
  2. We perform other tasks while it's working, such as downloading another page.
  3. We ask DownloadString for its results.
The WebClient class actually offers a built-in method called DownloadStringAsync which provides asynchronous-like functionality. For now, we'll ignore this and focus on the mechanism by which any method can be called asynchronously.

The third step is what makes asynchronous delegates useful. The caller rendezvous with the worker to get results and to allow any exception to be re-thrown. Without this step, we have normal multithreading. While it's possible to use asynchronous delegates without the rendezvous, you gain little over calling ThreadPool.QueueWorkerItem or using BackgroundWorker.

Here's how we can use asynchronous delegates to download two web pages, while simultaneously performing a calculation:

delegate string DownloadString (string uri);

static void ComparePages() {

// Instantiate delegates with DownloadString's signature:
DownloadString download1 = new WebClient().DownloadString;
DownloadString download2 = new WebClient().DownloadString;

// Start the downloads:
IAsyncResult cookie1 = download1.BeginInvoke (uri1, null, null);
IAsyncResult cookie2 = download2.BeginInvoke (uri2, null, null);

// Perform some random calculation:
double seed = 1.23;
for (int i = 0; i < 1000000; i++) seed = Math.Sqrt (seed + 1000);

// Get the results of the downloads, waiting for completion if necessary.
// Here's where any exceptions will be thrown:
string s1 = download1.EndInvoke (cookie1);
string s2 = download2.EndInvoke (cookie2);

Console.WriteLine (s1 == s2 ? "Same" : "Different");
}

We start by declaring and instantiating delegates for methods we want to run asynchronously. In this example, we need two delegates so that each can reference a separate WebClient object (WebClient does not permit concurrent access—if it did, we could use a single delegate throughout).

We then call BeginInvoke. This begins execution while immediately returning control to the caller. In accordance with our delegate, we must pass a string to BeginInvoke (the compiler enforces this, by manufacturing typed BeginInvoke and EndInvoke methods on the delegate type).

BeginInvoke requires two further arguments—an optional callback and data object; these can be left null as they're usually not required. BeginInvoke returns an IASynchResult object which acts as a cookie for calling EndInvoke. The IASynchResult object also has the property IsCompleted which can be used to check on progress.

We then call EndInvoke on the delegates, as their results are needed. EndInvoke waits, if necessary, until its method finishes, then returns the method's return value as specified in the delegate (string, in this case). A nice feature of EndInvoke is that if the DownloadString method had any ref or out parameters, these would be added into EndInvoke's signature, allowing multiple values to be sent back by to the caller.

If at any point during an asynchronous method's execution an unhandled exception is encountered, it's re-thrown on the caller's thread upon calling EndInvoke. This provides a tidy mechanism for marshaling exceptions back to the caller.

If the method you're calling asynchronously has no return value, you are still (technically) obliged to call EndInvoke. In a practical sense this is open to interpretation; the MSDN is contradictory on this issue. If you choose not to call EndInvoke, however, you'll need to consider exception handling on the worker method.

Asynchronous Methods

Some types in the .NET Framework offer asynchronous versions of their methods, with names starting with "Begin" and "End". These are called asynchronous methods and have signatures similar to those of asynchronous delegates, but exist to solve a much harder problem: to allow more concurrent activities than you have threads. A web or TCP sockets server, for instance, can process several hundred concurrent requests on just a handful of pooled threads if written using NetworkStream.BeginRead and NetworkStream.BeginWrite.

Unless you're writing a specialized high concurrency application, however, you should avoid asynchronous methods for a number of reasons:

  • Unlike asynchronous delegates, asynchronous methods may not actually execute in parallel with the caller
  • The benefits of asynchronous methods erodes or disappears if you fail to follow the pattern meticulously
  • Things can get complex pretty quickly when you do follow the pattern correctly

If you're simply after parallel execution, you're better off calling the synchronous version of the method (e.g. NetworkStream.Read) via an asynchronous delegate. Another option is to use ThreadPool.QueueUserWorkItem or BackgroundWorker—or simply create a new thread.

The WebClient class actually offers a built-in method called DownloadStringAsync which provides asynchronous-like functionality. For now, we'll ignore this and focus on the mechanism by which any method can be called asynchronously.

Asynchronous Events

Another pattern exists whereby types can provide asynchronous versions of their methods. This is called the "event-based asynchronous pattern" and is distinguished by a method whose name ends with "Async", and a corresponding event whose name ends in "Completed". The WebClient class employs this pattern in its DownloadStringAsync method. To use it, you first handle the "Completed" event (e.g. DownloadStringCompleted) and then call the "Async" method (e.g. DownloadStringAsync). When the method finishes, it calls your event handler. Unfortunately, WebClient's implementation is flawed: methods such as DownloadStringAsync block the caller for a portion of the download time.

The event-based pattern also offers events for progress reporting and cancellation, designed to be friendly with Windows applications that update forms and controls. If you need these features in a type that doesn't support the event-based asynchronous model (or doesn't support it correctly!) you don't have to take on the burden of implementing the pattern yourself, however (and you wouldn't want to!) All of this can be achieved more simply with the BackgroundWorker helper class.

(Sưu tầm)


 
 

 
     
 
Công nghệ khác:


Thread in C# - Part 12: Thread PoolingThread in C# - Part 11: ReaderWriterLock
Thread in C# - Part 10: BackgroundWorkerThread in C# - Part 9: Apartments and Windows Forms
Thread in C# - Part 8: Synchronization ContextsThread in C# - Part 7: Wait Handles
  Xem tiếp    
 
Lịch khai giảng của hệ thống
 
Ngày
Giờ
T.Tâm
TP Hồ Chí Minh
Hà Nội
 
   
New ADSE - Nhấn vào để xem chi tiết
Mừng Sinh Nhật Lần Thứ 20 FPT-APTECH
Nhấn vào để xem chi tiết
Bảng Vàng Thành Tích Sinh Viên FPT APTECH - Nhấn vào để xem chi tiết
Cập nhật công nghệ miễn phí cho tất cả cựu sinh viên APTECH toàn quốc
Tiết Thực Vì Cộng Đồng
Hội Thảo CNTT
Những khoảnh khắc không phai của Thầy Trò FPT-APTECH Ngày 20-11