Threading, Controls, Invoke and Marshaling to main thread

  • Thread starter Thread starter Charles Law
  • Start date Start date
C

Charles Law

Hi guys.

I have two threads: a main thread and a background thread. Lots of stuff
happens in the background thread that means I have to update several (lots)
of controls on a form.

It is quite tiresome to have to write code to call MyControl.Invoke for each
control on the form, along with the delegates that are required for each.

Is there a better way to do this? What I mean is, if I could marshal the
background thread back to the main thread before updating the controls, I
could access their properties without having to use Invoke. I could replace
lots of delegates and Invoke calls with a single function to marshal back to
the main thread.

Does anyone have any ideas?

TIA

Charles
 
I think that would overcomplicate things. I have a similar structure in my
code (with lots of different background threads, not executing concurrently,
but usually there is one background thread on the go).

What I have done is to create an interface that handles "callbacks" from
threads. The thread is derived from a base class that implements delegates
for these callbacks. So, my threads don't directly update the controls on
the main form, but execute delegates, passing in parameters at appropriate
times (including a "code" to identify the operation being performed). The
interface is implemented by the main form and any form that wishes to
execute these "worker threads".

The interface looks something like this:

Public Interface IWorkerThreadBase
Sub LabelOperation(ByVal theLabel As String, ByVal Min As Integer, ByVal
Max As Integer)
Sub StartedOperation(ByVal Parameters As WorkerParameters)
Sub PerformedOperation(ByVal nCurrentItem As Integer, ByVal Parameters
As WorkerParameters)
Sub FinishedOperation(ByVal Parameters As WorkerParameters)
Sub FailedOperation(ByVal Parameters As WorkerParameters)
Function GetForm() As System.Windows.Forms.Control
End Interface

and the base class for the threads defines the following operations:

Private Delegate Sub _LabelOperation_Delegate(ByVal theLabel As String,
ByVal Min As Integer, ByVal Max As Integer)
Private Delegate Sub _StartedOperation_Delegate(ByVal Parameters As
WorkerParameters)
Private Delegate Sub _PerformedOperation_Delegate(ByVal nCurrentItem As
Integer, ByVal Parameters As WorkerParameters)
Private Delegate Sub _FinishedOperation_Delegate(ByVal Parameters As
WorkerParameters)
Private Delegate Sub _FailedOperation_Delegate(ByVal Parameters As
WorkerParameters)

an example of which is....:

Public Sub PerformedOperation(ByVal theIndex As Integer)

Dim Parameters(1) As Object

Parameters(0) = theIndex
Parameters(1) = m_Parameters

Try

m_Manager.Form.GetForm().Invoke(New
_PerformedOperation_Delegate(AddressOf m_Manager.Form.PerformedOperation),
Parameters)
Catch ex As Exception

End Try

End Sub

Basically, each thread holds a parameters collection, which it updates
accordingly. This parameters collection is passed to the main form when the
delegate is executed on its thread. The mainform can indentify which thread
type has called it (m_Operation is an enumeration defined in the class) and
thus which parameters it needs to use to update its controls.

To give a concrete example, I have a thread that fetches thumbnails from a
database in the background. It is constantly running, looking for "slots"
in the global thumbnail cache that need filling with data. When it finds
one, it loads the thumbnail from the database, fills out appropriate
information in the parameters collection and then invokes the
"PerformedOperation" delegate. The main form then checks the parameters
collection to see which operation it is responding to and adds a new item to
the thumbnail list, updating any controls it needs to.


I hope this helps!
 
Hi Robin

Yes, it helps a great deal. The only thing is the 'overcomplicate things'
bit. You have identified a very tidy solution, but I just wonder if it is
not, in its way, as complicated as the original problem.

I was hoping that there would be some way of implementing the following

<MainThread>
Sub Main()

Create BackgroundThread

BackgroundThread.Start

Run Form

End Sub

Sub Callback(sender As Object, e As MyEventArgs)

Label1.Text = "Done"

End Sub
</MainThread>

<BackgroundThread>
Sub DoStuff()

OnStuffDone()

End Sub

Sub OnStuffDone()

_RaiseEventOnMainThread_ Callback(Me, New MyEventArgs)

End Sub
</BackgroundThread>

Obviously some of the above is real code, and some just wishful thinking,
but hopefully it illustrates the point. If this could be achieved it would
reduce the problem of updating controls on a form to that of a single
threaded application.

Charles
 
Yes, you've got it. Thats more or less what I'm doing. Instead of "raising
an event", I'm executing a delegate by "invoking it" from the thread. In
your case it would look something like this:

' Your delegate

Private Delegate Sub _RaiseEventOnMainThread_Delegate(Byref theThing As
MyThreadClass, ByVal theArgs as MyEventArgs)

' In your thread - NOTE: m_Form is the form you want to execute a delegate
on, thus, when you create a thread, you have to pass in the form to the
thread.....

Sub OnStuffDone()

' You parameters

Dim Parameters(2) As Object

Parameters(0) = Me
Parameters(1) = MyEventArgs

' Invoke the delegate on the main form "m_Form", passing in the
parameters.

Try
m_Form.Invoke(New _RaiseEventOnMainThread_Delegate(AddressOf
m_Form.OnStuffDone), Parameters)

Catch ex As Exception

End Try

End Sub
 
Hi Robin

Now I see. Thanks again.

Charles


Robin Tucker said:
Yes, you've got it. Thats more or less what I'm doing. Instead of "raising
an event", I'm executing a delegate by "invoking it" from the thread. In
your case it would look something like this:

' Your delegate

Private Delegate Sub _RaiseEventOnMainThread_Delegate(Byref theThing As
MyThreadClass, ByVal theArgs as MyEventArgs)

' In your thread - NOTE: m_Form is the form you want to execute a delegate
on, thus, when you create a thread, you have to pass in the form to the
thread.....

Sub OnStuffDone()

' You parameters

Dim Parameters(2) As Object

Parameters(0) = Me
Parameters(1) = MyEventArgs

' Invoke the delegate on the main form "m_Form", passing in the
parameters.

Try
m_Form.Invoke(New _RaiseEventOnMainThread_Delegate(AddressOf
m_Form.OnStuffDone), Parameters)

Catch ex As Exception

End Try

End Sub

controls from
 
Back
Top