2

I have a Worker class and a MainForm/UI class. From the UI class I create a new instance of the Worker class in a new background thread. This thread marshals a few updates back to the UI's controls. Since they are in different classes I basically pass the MainForm instance (this) and an appropriate delegate to update the controls, into the worker class' constructor. In the constructor I set the mainForm to an ISynchronizeInvoke object (call it _synch) and then, further down in the worker class I do _synch.Invoke(theDelegate, new object[] { "new value" }).

That all works fine, but then I realized that it is also possible to do just simply mainForm.Invoke (not using the ISynchronizeInvoke object). What is the difference between the two?

To make matters worse I read in an article that ISynchronizeInvoke is not really needed much anymore, now that SynchronizationContext has come a long. I realize that I do not understand what these two are for. Any help with understanding why I should use Invoke on these objects as opposed to directly on the mainForm would be greatly appreciated.

Anders
  • 580
  • 8
  • 17
  • ISynchronizeInvoke is only useful to another library that does *not* have any idea which particular thread is the UI thread or how to properly invoke to code that manipulates the UI. Never a problem with the Form class, it *is* the UI. And implements the plumbing that makes ISynchronizeInvoke work. Sadly WPF broke the contract so it isn't a general-purpose interface anymore. – Hans Passant Sep 24 '14 at 16:45
  • @HansPassant Thanks Hans. I tried to switch to `SynchronizationContext` as suggested by Sriram, but Send and Post do not accept custom delegates. They take a SendOrPostCallback delegate that only has one parameter (`object`). My current custom delegate has both a value string (the text I want the label to take) and a string lbName, so that the method can identify what label control to update. What do you recommend me doing here? Should i just stick to Form.Invoke? Thanks! – Anders Sep 24 '14 at 16:58
  • Using Invoke is wrong 99% of the time. Very prone to deadlock and races, always use BeginInvoke. A single object is enough to store *anything*, use small helper classes if necessary. A lambda is almost always the handy alternative. – Hans Passant Sep 24 '14 at 17:05
  • @HansPassant BeginInvoke did not work in my case, as I have several Invokes below each other (to update different labels in a `while(!mre.WaitOne(50))` loop. So if I used BeginInvoke the labels were stuttering, and not updating smoothly. I am not able to give a good technical explanation as to why. On the topic of SynchronizationContext it seems like a bad decision to go for that here, as I would have to create a helper class as mentioned (additional code/complexity), when I do not really get anything in return. Form.Invoke has the same effect as _synch.Send in this case. Would you not agree? – Anders Sep 24 '14 at 17:15

1 Answers1

4

In Winforms, No matter what method you call Form.Invoke, ISynchronizeInvoke.Invoke, SynchronizationContext.Send you are doing the same thing.

In fact they all internally goes to the same method which is Control.Invoke(which implements ISynchronizeInvoke interface member).

What is the difference between the two(Form.Invoke and ISynchronizeInvoke.Invoke)?

Nothing, they are pointing to same method Control.Invoke.

That said, if you depend on ISynchronizeInvoke you'll feel the pain when you're porting the application to another technology (say WPF). ISynchronizeInvoke is not supported there. It is the Winforms specific thing. You should always favour SynchronizationContext over anything.

SynchronizationContext provides the abstraction, whatever the technology may be, you can marshal the call to UI thread(typically not always) via SynchronizationContext. If your code depends on SynchronizationContext you can port to WPF or Asp.Net easily as they provide their technology specic implementation of SynchronizationContext.

  • Winforms implementation - WindowsFormsSynchronizationContext
  • Wpf/Silverlight implementation - DispatcherSynchronizationContext
  • Asp.net implementation - AspNetSynchronizationContext
  • Etc

One disadvantage of SynchronizationContext is that it provides no way to get the return value, though you can workaround it via closures, instance members etc.

Further reading: It's All About the SynchronizationContext

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Thanks! So I will forget about ISynchronizeInvoke. Regarding just using Form.Invoke, the downside is that Form.Invoke would not work if i switched to WPF (even if the controls there had the same name)? – Anders Sep 24 '14 at 15:29
  • I am getting this error in the constructor if I try to use SynchronizationContext as the object instead: `Cannot convert source type MainForm to target type SynchronizationContext`. Any idea? – Anders Sep 24 '14 at 15:34
  • @Anders You can't use the same control in wpf. You'll use the wpf specific equivalent control. Though you can use [Dispatcher.Invoke](http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke%28v=vs.110%29.aspx) in wpf, that means you need to modify your code. if `SynchronizationContext` was used it will work as it is. – Sriram Sakthivel Sep 24 '14 at 15:34
  • You can't assign it just like that, you need to use `SynchronizationContext.Current` property after creating the form. Refer this http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I – Sriram Sakthivel Sep 24 '14 at 15:36
  • Ah, thanks. Can the delegate only have one parameter? with Invoke I used a delegate that had a string value and a label name as string, that got sent back so the UI thread could update its correct control. Now I am getting an error that the `delegate is not assignable to parameter type SendOrPostCallback`. Thanks again! – Anders Sep 24 '14 at 16:01
  • @Anders Please take a look at lambda expressions. It will allow you to pass multiple parameters via closures. Or alternatively you can create a class which holds necessary information and use that. Hope that helps, if not please search on the same topic or feel free to ask new question as that's a new question. Future readers will be benefited if they can find it as a new question. – Sriram Sakthivel Sep 24 '14 at 18:30