Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make event callbacks into my win forms thread safe?

When you subscribe to an event on an object from within a form, you are essentially handing over control of your callback method to the event source. You have no idea whether that event source will choose to trigger the event on a different thread.

The problem is that when the callback is invoked, you cannot assume that you can make update controls on your form because sometimes those controls will throw an exception if the event callback was called on a thread different than the thread the form was run on.

like image 224
Simon Gillbee Avatar asked Aug 08 '08 17:08

Simon Gillbee


People also ask

What is Event in Windows Forms?

Load: This event occurs before a form is displayed for the first time. VisibleChanged: This event occurs when the Visible property value changes. Activated: This event occurs when the form is activated in code or by the user. Shown: This event occurs whenever the form is first displayed.

What is callback event C#?

In C#, you register a callback method for a given event by using the += operator: object. event += callback; Substitute object with any EFL object and event with the identifier of the event (such as PollHighEvt or TickEvt ). Set callback to the method to be invoked when the event occurs.


2 Answers

To simplify Simon's code a bit, you could use the built in generic Action delegate. It saves peppering your code with a bunch of delegate types you don't really need. Also, in .NET 3.5 they added a params parameter to the Invoke method so you don't have to define a temporary array.

void SomethingHappened(object sender, EventArgs ea) {    if (InvokeRequired)    {       Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);       return;    }     textBox1.Text = "Something happened"; } 
like image 60
Jake Pearson Avatar answered Sep 19 '22 02:09

Jake Pearson


Here are the salient points:

  1. You can't make UI control calls from a different thread than the one they were created on (the form's thread).
  2. Delegate invocations (ie, event hooks) are triggered on the same thread as the object that is firing the event.

So, if you have a separate "engine" thread doing some work and have some UI watching for state changes which can be reflected in the UI (such as a progress bar or whatever), you have a problem. The engine fire's an object changed event which has been hooked by the Form. But the callback delegate that the Form registered with the engine gets called on the engine's thread… not on the Form's thread. And so you can't update any controls from that callback. Doh!

BeginInvoke comes to the rescue. Just use this simple coding model in all your callback methods and you can be sure that things are going to be okay:

private delegate void EventArgsDelegate(object sender, EventArgs ea);  void SomethingHappened(object sender, EventArgs ea) {    //    // Make sure this callback is on the correct thread    //    if (this.InvokeRequired)    {       this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });       return;    }     //    // Do something with the event such as update a control    //    textBox1.Text = "Something happened"; } 

It's quite simple really.

  1. Use InvokeRequired to find out if this callback happened on the correct thread.
  2. If not, then reinvoke the callback on the correct thread with the same parameters. You can reinvoke a method by using the Invoke (blocking) or BeginInvoke (non-blocking) methods.
  3. The next time the function is called, InvokeRequired returns false because we are now on the correct thread and everybody is happy.

This is a very compact way of addressing this problem and making your Forms safe from multi-threaded event callbacks.

like image 36
Simon Gillbee Avatar answered Sep 19 '22 02:09

Simon Gillbee