Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make an eventhandler run asynchronously?

I am writing a Visual C# program that executes a continuous loop of operations on a secondary thread. Occasionally when that thread finishes a task I want it to trigger an eventhandler. My program does that but the when the event handler is triggered, the secondary thread waits until the event handler is finished before continuing the thread. How do I make it continue? Here is the way I currently have it structured...

class TestClass  {   private Thread SecondaryThread;   public event EventHandler OperationFinished;    public void StartMethod()   {     ...     SecondaryThread.Start();      //start the secondary thread   }    private void SecondaryThreadMethod()   {     ...     OperationFinished(null, new EventArgs());     ...  //This is where the program waits for whatever operations take          //place when OperationFinished is triggered.   }  } 

This code is part of an API for one of my devices. When the OperationFinished event is triggered I want the client application to be able to do whatever it needs to (i.e. update the GUI accordingly) without haulting the API operation.

Also, if I do not want to pass any parameters to the event handler is my syntax correct by using OperationFinished(null, new EventArgs()) ?

like image 994
PICyourBrain Avatar asked Dec 16 '09 17:12

PICyourBrain


People also ask

Can event handlers be async?

The solution is extremely simple: just mark the event handler as async. You should never use async void methods, instead use async Task or async Task<T1,...>. The exception, apparently, is event handlers. And it kind of makes sense: an event handler is designed to be called asynchronously.

Which async method can be used for event handlers?

Since delegates (and events are delegates) implement the Asynchronous Programming Model (APM), you could use the TaskFactory. FromAsync method.

Are events Async C#?

Yes, they are synchronous.

Is an EventHandler a delegate?

The EventHandler delegate is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must use the generic EventHandler<TEventArgs> delegate class.


2 Answers

So you want to raise the event in a manner that prevents the listeners from blocking the background thread? Gimme a couple minutes to whip up an example; it's pretty simple :-)

Here we go: first an important note! Whenever you call BeginInvoke you must call the corresponding EndInvoke, otherwise if the invoked method threw an exception or returned a value then the ThreadPool thread will never be released back to the pool, resulting in a thread-leak!

class TestHarness {      static void Main(string[] args)     {         var raiser = new SomeClass();          // Emulate some event listeners         raiser.SomeEvent += (sender, e) => { Console.WriteLine("   Received event"); };         raiser.SomeEvent += (sender, e) =>         {             // Bad listener!             Console.WriteLine("   Blocking event");             System.Threading.Thread.Sleep(5000);             Console.WriteLine("   Finished blocking event");         };          // Listener who throws an exception         raiser.SomeEvent += (sender, e) =>         {             Console.WriteLine("   Received event, time to die!");             throw new Exception();         };          // Raise the event, see the effects         raiser.DoSomething();          Console.ReadLine();     } }  class SomeClass {     public event EventHandler SomeEvent;      public void DoSomething()     {         OnSomeEvent();     }      private void OnSomeEvent()     {         if (SomeEvent != null)         {             var eventListeners = SomeEvent.GetInvocationList();              Console.WriteLine("Raising Event");             for (int index = 0; index < eventListeners.Count(); index++)             {                 var methodToInvoke = (EventHandler)eventListeners[index];                 methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, null);             }             Console.WriteLine("Done Raising Event");         }     }      private void EndAsyncEvent(IAsyncResult iar)     {         var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;         var invokedMethod = (EventHandler)ar.AsyncDelegate;          try         {             invokedMethod.EndInvoke(iar);         }         catch         {             // Handle any exceptions that were thrown by the invoked method             Console.WriteLine("An event listener went kaboom!");         }     } } 
like image 59
STW Avatar answered Oct 10 '22 23:10

STW


With the Task Parallel Library it is now possible to do the following:

Task.Factory.FromAsync( ( asyncCallback, @object ) => this.OperationFinished.BeginInvoke( this, EventArgs.Empty, asyncCallback, @object ), this.OperationFinished.EndInvoke, null ); 
like image 34
WhiteKnight Avatar answered Oct 10 '22 22:10

WhiteKnight