Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VB.NET Two different approaches to generic cross-threaded operations; which is better?

VB.NET 2010, .NET 4

Hello,

I recently read about using SynchronizationContext objects to control the execution thread for some code. I have been using a generic subroutine to handle (possibly) cross-thread calls for things like updating UI controls that utilize Invoke. I'm an amateur and have a hard time understanding the pros and cons of any particular approach. I am looking for some insight on which approach might be preferable and why.

Update: This question is motivated, in part, by statements such as the following from the MSDN page on Control.InvokeRequired.

An even better solution is to use the SynchronizationContext returned by SynchronizationContext rather than a control for cross-thread marshaling.

And also, general confusion as to why, as I look around, a majority of answers to questions regarding this type of problem on SO suggest the Invoke approach without mentioning this method.

Method 1:

Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))
    If Control.InvokeRequired Then
        Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
    Else
        Action(Control)
    End If
End Sub

Method 2:

Public Sub UIAction(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of Control))
    SyncContext.Send(New Threading.SendOrPostCallback(Sub() Action(Control)), Nothing)
End Sub

Where SyncContext is a Threading.SynchronizationContext object defined in the constructor of my UI form (I store it in a module... Not sure if that's the best choice):

Public Sub New()
    InitializeComponent()
    SyncContext = WindowsFormsSynchronizationContext.Current
End Sub

Then, if I wanted to update a control (e.g., Label1) on the UI form, I would do:

InvokeControl(Label1, Sub(x) x.Text = "hello")

or

UIAction(Label1, Sub(x) x.Text = "hello")

So, what do y'all think? Is one way preferred or does it depend on the context? If you have the time, verbosity would be appreciated!

Thanks in advance,
Brian

like image 605
Brian Mulcahy Avatar asked Oct 12 '22 16:10

Brian Mulcahy


1 Answers

Well, I've been doing some reading and, since I'm not getting any responses, I figured I'd start a partial answer to my own question containing what I've found so far:

I found an interesting codeproject article discussing the use of SynchronizationContext for marshaling code between threads (and specifically from worker threads to the UI thread). Some observations I found interesting:

  • The UI thread's SynchronizationContext object is created upon creation of the first control in that thread. Before that, it is not defined.
  • The SynchronizationContext for the UI thread is not an instance of the SynchronizationContext class, but of the System.Windows.Forms.WindowsFormsSynchronizationContext class which is derived from SynchronizationContext. It is this class that defines the behavior of Post/Send allowing marshaling of code from one thread to another.
  • An appeal of passing the UI thread's SynchronizationContext around rather than using Invoke is that you don't have to keep a reference to your UI form in logic in order to invoke it.
  • The Post method seems appealing for accomplishing things like indicator updates since it's non-blocking, but, as the article points out, exceptions thrown in posted code are thrown in the UI thread. i.e., a bug in code posted to the UI can crash the UI. Send doesn't have this problem. Exceptions thrown when sending are thrown in the work thread.

Update: Here is another insightful article. In this article, Kael Rowan discusses a context in which using SynchronizationContext might be preferable to a control instance's Invoke/BeginInvoke methods. He argues that, when writing a reusable library, it is not desirable to have to maintain a reference to a control outside of the library simply for invokation purposes. He provides code for a delegate that ensures any new thread created will share the UI thread's SynchronizationContext.

Alright, well, it looks like I'm not going to get any more comments here. What I've written here is about as close as my ignorance allows me to get to an answer. If anyone has anything else to add, I'd surely appreciate it, but I'm moving on for now. :/

like image 116
Brian Mulcahy Avatar answered Oct 15 '22 11:10

Brian Mulcahy