In Part 1, we began to examine delegates as they applied to multi-threading scenarios.  However, the specific implemenation was not necessarily an optimal one.  The strategy implemented was to periodically check to see if the work was completed or not.  Once it was, EndInvoke was called, and execution continued.  This was a "Daddy, are we there yet?" strategy.  It's as annoying in your code as it is in the car.  There's a better way.  In fact, there are 2. 

The first is very simple.  EndInvoke will block the calling thread until the work is complete.  Let's take another physical world example.  Let's assume it's coming up on lunch time, and you and a coworker are going to Chipotle (because there's just no other place you'd ever want to go.)  Unfortunately, he's in a meeting that's running long, and you have to get some code finished before you leave.  How would this look if it was delegate / asynchronous code?

class TimeForLunch
{
    private delegate void WaitForMeetingToEndHandler();

    static void Main(string[] args)
    {
        WaitForMeetingToEndHandler waitForMeeting = 
            new WaitForMeetingToEndHandler(WaitForMeetingToEnd);

        // Start waiting on your coworker
        IAsyncResult result = waitForMeeting.BeginInvoke(null, null);

        // Keep working on your project
        FinishCode();

        // Check to see if your coworker's meeting is out.  If not, wait until it is.
        waitForMeeting.EndInvoke(result);

        // Both tasks are complete.  It's burrito-time.
        Console.WriteLine("Go to lunch.");

        Console.ReadLine();
    }

    static void WaitForMeetingToEnd()
    {
        Thread.Sleep(10000);
        Console.WriteLine("Meeting ended");
    }

    static void FinishCode()
    {
        Thread.Sleep(1000);
        Console.WriteLine("Code complete");
    }
}

Finally, there is one other strategy to use.  Suppose you are working on a customer presentation.  You're ready to go, but you can't send it off to the client until your finance department finishes running some numbers for you.  You ask them to let you know as soon as that is complete, so you can send off the presentation.  The distinction here is that you don't really have any simultaneous work to be doing.  You just need to wait on them, and then kick off the email.  We'll start with the code:

class Presentation
{
    private delegate DataTable WaitForFinanceHandler();
    static void Main(string[] args)
    {
        Console.WriteLine("Tell Finance to finish, and get back to me.");
        WaitForFinanceHandler waitForFinance = FinishFinanceNumbers;
        Console.WriteLine("When done, take me to the SendEmail function");
        waitForFinance.BeginInvoke(SendEmail, null);
        Console.ReadLine();
    }

    static void SendEmail(IAsyncResult result)
    {
        WaitForFinanceHandler waitForFinance =
            (WaitForFinanceHandler) ((AsyncResult) result).AsyncDelegate;
        DataTable financeResults = waitForFinance.EndInvoke(result);
        Console.WriteLine("Finance Results received, emailing...");
    }

    static DataTable FinishFinanceNumbers()
    {
        Thread.Sleep(2000);
        Console.WriteLine("Finance team finished, returning results");
        return new DataTable();
    }
}

 So there are a few minor changes here.  First, rather than wait on the worker thread to complete in Main, we've opted to ask the thread to notify us by calling back to a different method, SendEmail.  Perhaps you've noticed that Intellisense has indicated 2 parameters to BeginInvoke that we have not previously used when calling BeginInvoke.  The first of these is actually another delegate that represents the method to call when the work completes.  The signature for this method is hard coded. 

void METHOD_NAME(IAsyncResult result)

Things get a bit more complicated here.  You see, you need to be able to recover the delegate that you declared in Main, so that you can call EndInvoke (thus getting the function's return value), as we've done in our previous examples.  This is a two step process, and it isn't intuitive.  The property you are looking for is AsyncDelegate, but the IAsynResult interface does not define it.  2 steps are needed in order to get to your delegate:

1. Add this using statement to your code:

using System.Runtime.Remoting.Messaging;

2. You will need to cast the IAsyncResult to an AsyncResult object.  Finally, you will then cast the AsyncDelegate property to a delegate of your type. 

WaitForFinanceHandler waitForFinance =
   (WaitForFinanceHandler) ((AsyncResult) result).AsyncDelegate;

Certainly there is a reason for this headache, but with Generics available, I don't see it off the top of my head.  At any rate, once you have the original delegate, and the IAsyncResult, you have the same tools as in the previous example.  A call to EndInvoke will yield your result.

One last thing.  The code above works, but only just barely.  Recall that using delegates for asynchronous calls uses the Threadpool behind the scenes.  Further, recall that the Threadpool's Threads are all Background threads.  Therefore, when the foreground thread (Main) completes, the background thread is killed.  So if I had not placed Console.ReadLine() at the very end, the program would have ended without waiting for finance or sending my report at all. 

That's it for delegates right now.  There are a few more things to learn, but my goal here was not to repeat what I had already read, but to try and present the basic concepts in a manner that was more tangible.  If the concepts now make sense, the rest of the articles on the net will hopefully come more naturally.