Tags: , , | Posted by Kevin Babcock on 7/27/2009 9:17 AM | Comments (0)

threads  There are certainly powerful and complex multithreading capabilities built into the .NET Framework. But if you’re looking for an easy way to perform asynchronous operations in your applications, delegates are a quick solution. Delegates offer easy-to-use methods that abstract most of the threading details you would otherwise need to be worry about, queuing up work for thread pool worker threads that get dispatched as soon as a thread is available. In this tutorial I’ll give you a quick guide to using asynchronous delegates in your own applications.

Creating and invoking anonymous delegates

In C# 3.0 we now have lambda expressions, which allow us to write anonymous delegates in a much more succinct way. Consider the following delegate, which when called will be used to retrieve a list of categories from the database:

Func<IList<Category>> GetCategories = () => NorthwindData.GetCategories();

In the code above I simply call a method in my persistence layer, NorthwindData.GetCategories(), which returns a collection of Category objects encapsulating the category data in a database table. A few weeks ago I blogged about Func<TResult>, a built-in generic delegate that was shipped as part of v3.5 of the .NET Framework. Calling the delegate method is fairly straight-forward: I can either call it directly or utilize it’s Invoke() method.

categories = GetCategories();
categories = GetCategories.Invoke();

Invoking anonymous delegates asynchronously

However, calling my delegate method is a synchronous operation and blocks all other processing on the current thread until its work is done. A multi-threaded approach might be worth considering if I want to let the calling thread continue working asynchronously. With delegates, doing so is quite easy with the BeginInvoke() and EndInvoke() methods. Using BeginInvoke() does not spawn a new thread, but instead causes the delegate to be placed in the thread pool queue. A worker thread is assigned to perform the work as soon as one becomes available.

Func<IList<Category>> GetCategories = () => NorthwindData.GetCategories();
var result = GetCategories.BeginInvoke(null, null);

// do other work
var categories = GetCategories.EndInvoke(result);

There are some important things to note about the code above. First, calling BeginInvoke() does not return the object indicated by the method signature of the invoked delegate. Instead, it returns an IAsyncResult object, which holds a reference to and status information for an asynchronous operation. It expects at least two parameters: an AsyncCallback delegate which will be executed once the method has completed, and a user-defined object that is passed into the callback method. If the delegate method being called expects any parameters, those should be included before the callback delegate and user-defined object. As you can see, passing null in to both required parameters is perfectly valid if you don’t care to use them. Here’s how you might call BeginInvoke() using the expected parameters:

var result = GetCategories.BeginInvoke(r =>
{
    var invokedDelegate = (Func<IList<Category>>)r.AsyncState;
    var categories = invokedDelegate.EndInvoke(r);

    // do something with the results
}, GetCategories);

I have passed in a lambda expression for the callback delegate and a reference to the calling delegate as my second parameter. By passing GetCategories in as the second parameter, I have access to it from the IAsyncResult.AsyncState property. However, this is all an unnecessary exercise. Thanks to lambda expressions and closures, I can access the calling delegate without having to pass it in explicitly and then perform the explicit cast. Here’s a better version:

var result = GetCategories.BeginInvoke(r =>
{
var categories = GetCategories.EndInvoke(r);

// do something with the results

}, null);

Here’s an example of calling a delegate that expects parameters:

Func<IList<Product>> GetExpensiveProducts = price => NorthwindData.GetProductsThatCostMoreThan(price);
var price = 75.5m;
var result = GetExpensiveProducts.BeginInvoke(price, 
    r =>
    {
        var products = GetExpensiveProducts.EndInvoke(r);

        // do something with the results
        
    }, null);

As you have probably already figured out, in order to get a result from the method I must call the delegate’s EndInvoke() method and pass in the IAsyncResult returned when the delegate was invoked. So why the need to call EndInvoke() in the first place? Why not just make the results available in the callback passed into BeginInvoke()? Well, one important reason is that the developer may want control over what gets done while the asynchronous method is hard at work on something else. So if my application is doing some work but comes to a point where it needs the results of my asynchronous method I can call EndInvoke(), which will return the results to me. If the results are not yet available, EndInvoke() will block the thread until the asynchronous method has finished what it’s doing.

So far we’ve talked about managing asynchronous delegate calls using EndInvoke() to block the thread until the call completes or by passing in a callback method to BeginInvoke(), which will be executed on another thread when the method call completes. There are two other common approaches to invoking methods asynchronously as well: poll the IAsyncResult to determine when it has completed; or retrieve the IAsyncResults.AsyncWaitHandle to call its WaitOne() method and block execution until the wait handle is signaled. To learn more about these other methods, I recommend this great article written back in 2003 (and still relevant!) by Richard Blewett.

Calling multicast delegates asynchronously

I’d like to finish off this post with a more realistic example of asynchronous delegates in action, but first I need to mention one important fact that you should know. If you are using multicast delegates you must call BeginInvoke() and EndInvoke() on each member of the delegate’s invocation list. Calling them on a multicast delegate (and not its individual member) will result in an ArgumentException. Here’s an example of how you would need to call multicast delegates asynchronously:

Func<IList<Category>> GetCategories = null;
GetCategories += () => NorthwindData.GetCategories();
GetCategories += () => NorthwindData.GetCategoriesWithOutOfStockProducts();
var jobs = GetCategories.GetInvocationList();
foreach (var job in jobs)
{
var work = (Func<IList<Category>>)job;
var result = work.BeginInvoke(r =>
{
// do work here, then grab result
var categories = work.EndInvoke(r);
// do more stuff here
}, null);
}

An example

Since I’m a web guy, I’ll use a real simple ASP.NET application as the basis for this example. Consider the following page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AsynchronousDelegates._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Example - Asynchronous Delegates</title>
<style type="text/css">
.container { float: left; margin-left: 10px; padding: 10px 0 10px 0; }
</style>
</head>
<body>
<form id="form1" runat="server">
<div class="container">
<div>Categories</div>
<div>
<asp:ListBox ID="lbCategories" runat="server"
DataTextField="CategoryName"
DataValueField="CategoryID"
Rows="10">
</asp:ListBox>
</div>
</div>
<div class="container">
<div>Out of Stock</div>
<div>
<asp:ListBox ID="lbOutOfStockCategories" runat="server"
DataTextField="CategoryName"
DataValueField="CategoryID"
Rows="10">
</asp:ListBox>
</div>
</div>
</form>
</body>
</html>

The page is quite basic, but I want to use it to display a list of all product categories as well as those categories with products that are out of stock. This should be simple enough. Here’s how the page might retrieve the data and bind it to the ListBox controls:

using System;
using System.Collections.Generic;
using MyViewState.Examples.Data;
using MyViewState.Examples.Entities;

namespace AsynchronousDelegates
{
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs args)
{
base.OnInit(args);

lbCategories.PreRender += new EventHandler((s, e) =>
{
var categories = NorthwindData.GetCategories();
lbCategories.DataSource = categories;
lbCategories.DataBind();
});

lbOutOfStockCategories.PreRender += new EventHandler((s, e) =>
{
var categories = NorthwindData.GetCategoriesWithOutOfStockProducts();
lbOutOfStockCategories.DataSource = categories;
lbOutOfStockCategories.DataBind();
});
}
}
}

But just imagine that this page is a bit more complex than I’ve presented here, and that it takes a few seconds to process the entire page on the server. Also imagine that results we want require some heavy processing on the database and take an additional second or two. A nice way to let the page do its processing without stopping to wait for the database might be with the use of asynchronous delegates.

using System;
using System.Collections.Generic;
using MyViewState.Examples.Data;
using MyViewState.Examples.Entities;

namespace AsynchronousDelegates
{
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs args)
{
base.OnInit(args);
Func<IList<Category>> GetCategories = null;
GetCategories += () => NorthwindData.GetCategories();
GetCategories += () => NorthwindData.GetCategoriesWithOutOfStockProducts();
var jobs = GetCategories.GetInvocationList();
var results = new IAsyncResult[jobs.Length];
for (int i = 0; i < jobs.Length; i++)
{
var work = (Func<IList<Category>>)jobs[i];
var result = work.BeginInvoke(null, null);
results[i] = result;
}

lbCategories.PreRender += new EventHandler((s, e) =>
{
var work = (Func<IList<Category>>)jobs[0];
var categories = work.EndInvoke(results[0]);
lbCategories.DataSource = categories;
lbCategories.DataBind();
});

lbOutOfStockCategories.PreRender += new EventHandler((s, e) =>
{
var work = (Func<IList<Category>>)jobs[1];
var categories = work.EndInvoke(results[1]);
lbOutOfStockCategories.DataSource = categories;
lbOutOfStockCategories.DataBind();
});
}

// Handle events, process page, etc...
}
}

As you can see, during page initialization I’ve created a multicast delegate which calls two methods in my persistence layer. I’ve also added PreRender event handlers to the ListBox controls that are going to display the results. I can go ahead and fire off the data retrieval methods asynchronously (@ line 21) and then call EndInvoke() (@ line 28 & 36) to wait for the results during the PreRender event of each ListBox control. This adds additional complexity to my page, but what I’ve gained in return is a bit of performance. Instead of waiting for the database calls to return, I can continue processing the page until I get to the point where I need the results.

 Internet Explorer screen shot

Wrap up

Obviously, asynchronous delegates can be used by just about any kind of .NET application. They offer a quick and simple asynchronous solution, but suffer from the limitations of the thread pool servicing the requests. Remember, ASP.NET uses the thread pool too, so if you use the example above then every thread you use is one less thread that can be used by ASP.NET to service other requests. If you need more complex threading capabilities or a higher level of control over the threads in your application, I recommend learning the ins and outs of threading in .NET. You can start with this tutorial.

Enjoy!

[Source Code]

kick it on DotNetKicks.com

Add comment




biuquote
  • Comment
  • Preview
Loading