Using Delegates in C# Asynchronously

13,328

Solution 1

See Calling Synchronous Methods Asynchronously

The BeginInvoke will return an IAsyncResult, which enables a number of different ways to be aware of when it is done, such as using its AsyncWaitHandle.WaitOne() method. It depends on what you are doing in the meantime.

Or you can pass a delegate for a callback method to BeginInvoke. This is arguably the most powerful strategy, but sometimes is overkill.

Solution 2

Calling EndInvoke on the returned IAsyncResult reference is very important. It is the only way to find out if the delegate target finished executing without any exceptions. If you don't, such an exception will fall into the bit-bucket and your program will silently fail to execute properly. You can call EndInvoke either on the same thread that called BeginInvoke() or you can do it in a callback. Calling it on the same thread rarely is useful, you'd almost always lose the benefits of asynchronous execution. Some sample code that demonstrates both and emphasizes the exception handling:

using System;
using System.Runtime.Remoting.Messaging;

class Program {
  static void Main(string[] args) {
    new Program().Run();
    Console.ReadLine();
  }
  void Run() {
    Action example = new Action(threaded);
    IAsyncResult ia = example.BeginInvoke(new AsyncCallback(completed), null);
    // Option #1:
    /*
    ia.AsyncWaitHandle.WaitOne();
    try {
      example.EndInvoke(ia);
    }
    catch (ApplicationException ex) {
      Console.WriteLine(ex.Message);
    }
    */
  }

  void threaded() {
    throw new ApplicationException("Kaboom");
  }

  void completed(IAsyncResult ar) {
    // Option #2:
    Action example = (ar as AsyncResult).AsyncDelegate as Action;
    try {
      example.EndInvoke(ar);
    }
    catch (ApplicationException ex) {
      Console.WriteLine(ex.Message);
    }
  }
}

You should not use a try block in the completion callback if you don't expect the code to throw exceptions. This ensures your program terminates when it does.

Share:
13,328
etoisarobot
Author by

etoisarobot

Updated on June 04, 2022

Comments

  • etoisarobot
    etoisarobot almost 2 years

    I have the following delegate

    delegate void UpdateFileDelegate(long maxFileID);
    

    That I am calling from a WinForms app like so

    UpdateFileDelegate FD = new UpdateFileDelegate(ClassInstance.UpdateFile);
    FD.BeginInvoke(longIDNumber,null,null);
    

    It runs asynchronously but the question I have is how can I tell when the Method is done executing so I can let the end user know?

    Update: Thanks to the recommendations below the following code does the trick. Also this article was helpful in getting me to understand what my code is actually doing.

    delegate void UpdateFileDelegate(long maxFileID);
    UpdateFileDelegate FB = new UpdateFileDelegate(ClassInstance.UpdateFile);
    AsyncCallback callback = new AsyncCallback(this.CallBackMethod);
    IAsyncResult result = FB.BeginInvoke(longIDNumber);
    
    private void CallBackMethod(IAsyncResult result)
        {
         AsyncResult delegateResult = (AsyncResult)result;
    
         UpdateFileDelegate fd = (UpdateFileDelegate)delegateResult.AsyncDelegate;
         fd.EndInvoke(result);
         MessageBox.Show("All Done!");
        }
    
  • supercat
    supercat about 12 years
    What's the proper pattern if code decides that it isn't going to care about what happens with an asynchronous task (e.g. because a user clicked "cancel"), but a "cancel" request isn't immediately responsive? Simply arrange to have its callback call EndInvoke and die, and then abandon it (figuring the callback will be kept alive until the task finishes or processes the cancel request, whereupon the callback will execute and clean up the IAsyncResult?