Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why there is no EndInvoke in Cross thread UI component call ?

Tags:

c#

I have been trying to add some string to some ListBox that i have on my application ( simple winform ) - and i did it in using BeginInboke

 myListBox.BeginInvoke(new Action(delegate()
 {
       myListBox.Items.Add( "some string" )); 
 }));

After i reading those 3 lines again - and i don't understand why in any example of Cross thread UI that i look on google and on MSDN i don't see any call of EndInvoke ? Is there some reason to not call the EndInvoke on this case ?

like image 653
Yanshof Avatar asked Aug 02 '13 12:08

Yanshof


2 Answers

This was an unfortunate naming choice in .NET. The Control.BeginInvoke and Dispatcher.BeginInvoke methods have the same name as a delegate's methods but operate completely different. The chief differences:

  • A delegate's BeginInvoke() method is always type-safe, it has the exact same arguments as the delegate declaration. This is entirely missing from the Control/Dispatcher versions, arguments are passed through a params array of type object[]. The compiler will not tell you when you get an argument wrong, it bombs at runtime

  • A delegate's Invoke() method runs the delegate target on the same thread. Not the case for Control/Dispatcher.Invoke(), they marshal the call to the UI thread

  • An exception that's thrown in a delegate's BeginInvoke() target is captured and does not cause the program to fail. To be re-thrown when you call EndInvoke(). This is not the case at all for Control/Dispatcher.BeginInvoke(), they raise the exception on the UI thread. With no decent way to catch the exception, one of the bigger reasons that Application.UnhandledException exists.

  • Calling a delegate's EndInvoke() method is required, it causes a 10 minutes resource leak if you don't. It is not required for the Control/Dispatcher.BeginInvoke() methods and you never do so in practice.

  • Using Control/Dispatcher.Invoke() is risky, it is quite liable to cause deadlock. Triggered when the UI thread isn't ready to invoke the target and does something unwise like waiting for a thread to complete. Not a problem for a delegate, not in the least because its Invoke() method doesn't use a thread.

  • Calling Control/Dispatcher.BeginInvoke() on the UI thread is a supported scenario. The target still runs on the UI thread, as expected. But later, after the UI thread goes idle again and re-enters the dispatcher loop. This is actually a very useful feature, it helps solve tricky re-entrancy problems. Particularly in event handlers for UI controls that will misbehave when you run code with too many side-effects.

A big list with heavy implementation details. The TLDR version is certainly: "They have nothing in common, not calling EndInvoke is fine and entirely normal".

like image 183
Hans Passant Avatar answered Oct 22 '22 03:10

Hans Passant


Control.BeginInvoke does not appear to fully follow the usual BeginX/EndX pattern a.k.a. the Asynchronous Programming Model (APM). Usually, you must call EndX for each BeginX, but in the case of Control.BeginInvoke, this is not strictly required:

"You can call EndInvoke to retrieve the return value from the delegate, if neccesary, but this is not required. EndInvoke will block until the return value can be retrieved."

— from the Remarks section on the MSDN reference page for Control.BeginInvoke (emphasis by me)

And in practice, it is hardly ever necessary. This is because the method is usually called to let some code execute on the UI thread that updates the UI. Updating the UI won't normally produce any return value, therefore you wouldn't want to call EndInvoke.

like image 30
stakx - no longer contributing Avatar answered Oct 22 '22 03:10

stakx - no longer contributing