By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,758 Members | 1,281 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,758 IT Pros & Developers. It's quick & easy.

Threading, Controls, Invoke and Marshaling to main thread

P: n/a
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
Nov 20 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
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!
"Charles Law" <bl***@nowhere.com> wrote in message
news:Oj**************@TK2MSFTNGP11.phx.gbl...
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

Nov 20 '05 #2

P: n/a
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
"Robin Tucker" <id*************************@reallyidont.com> wrote in
message news:c7*******************@news.demon.co.uk...
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!
"Charles Law" <bl***@nowhere.com> wrote in message
news:Oj**************@TK2MSFTNGP11.phx.gbl...
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


Nov 20 '05 #3

P: n/a
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

"Charles Law" <bl***@nowhere.com> wrote in message
news:OQ**************@tk2msftngp13.phx.gbl...
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
"Robin Tucker" <id*************************@reallyidont.com> wrote in
message news:c7*******************@news.demon.co.uk...
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!
"Charles Law" <bl***@nowhere.com> wrote in message
news:Oj**************@TK2MSFTNGP11.phx.gbl...
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



Nov 20 '05 #4

P: n/a
Hi Robin

Now I see. Thanks again.

Charles
"Robin Tucker" <id*************************@reallyidont.com> wrote in
message news:c7*******************@news.demon.co.uk...
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

"Charles Law" <bl***@nowhere.com> wrote in message
news:OQ**************@tk2msftngp13.phx.gbl...
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
"Robin Tucker" <id*************************@reallyidont.com> wrote in
message news:c7*******************@news.demon.co.uk...
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!
"Charles Law" <bl***@nowhere.com> wrote in message
news:Oj**************@TK2MSFTNGP11.phx.gbl...
> 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
>
>



Nov 20 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.