How does the main thread know when the work thread is finished?

K

Kueishiong Tu

I have a window form application. It requires to retrieve
data from a web site. Since the web request is very time
consuming, so I create a work thread to do the job.
How does the window main form (which is the main thread)
know when the work thread has finished the job so that
it can display the data received on the form? I do not
want to use a global variable since if the main form is
continuously checking for the global variable, it will
not be able to process the window UI events. I try to
raise an event when the work thread's job is done,
but how can the main thread detected this event?
 
W

Wiktor Zychla

I have a window form application. It requires to retrieve
data from a web site. Since the web request is very time
consuming, so I create a work thread to do the job.
How does the window main form (which is the main thread)
know when the work thread has finished the job so that
it can display the data received on the form? I do not
want to use a global variable since if the main form is
continuously checking for the global variable, it will
not be able to process the window UI events. I try to
raise an event when the work thread's job is done,
but how can the main thread detected this event?

it should add its own listener to the list of event listeners.
 
W

William Ryan

Here is an exerpt from an article I'm writing.... When I'm done, I raise
FillIt (Ignore the FillComplete, that's actually what I'm going to
ultimately use) Fillit is being hanlded by Notify...This isn't the best
example, but it should get you through what you want. Remember though,
please remember, Forms aren't thread safe!!!!!!!!! I've dropped the ball on
that a few times...

Now, declare some module level variables:



Const cs As String = "data source=----;initial catalog=--;integrated
security=SSPI "

Dim SqlDataAdapter1 As New System.Data.SqlClient.SqlDataAdapter

Dim dt As New DataTable

Dim cn As New SqlConnection(cs)



Now, here's the code that fires the query:



Dim th As Thread 'Declare a new Thread

Dim asy As New AsynchGrid 'AsnychGrid is the Class we are going to use

asy.PatientDataAdapter = SqlDataAdapter1 '

Dim cmd As New SqlCommand("SELECT * FROM Where Created_Time > GetDate()-30",
cn)

SqlDataAdapter1.SelectCommand = cmd

asy.PatientDataSet = dt

Label1.Text = "Starting"



AddHandler asy.FillIt, AddressOf Notify

Dim tsFill As ThreadStart = New ThreadStart (AddressOf asy.FillPatient)

th = New Thread(tsFill)

th.Name = "Filler Thread"

th.Start()



Now, understand what we want to accomplish. We want to fire a query and
then give control back to the user. Whenever the query is returned, we'll
notify the caller that it's done and then do whatever we need to with the
query results. But we want the user to be able to do other things while
this is happening. So, Notify is a Subroutine that I have that will fill a
ListBox control with some of the data from the query. This snippet really
doesn't matter, we could do just about anything here.I just chose this for
the sake of illustration:



Private Sub Notify()

Dim dr As DataRow

ListBox1.Items.Clear()

ListBox1.BeginUpdate()'This will speed up the ListBox Fill

For Each dr In dt.Rows

ListBox1.Items.Add(dr(1).ToString)

Next

ListBox1.EndUpdate() 'We're done filling it so we can end it

End Sub



Now, I have a class called AysnchGrid (I was initially binding to a
DataGrid.sorry about the counterintuitive name) that has the following
private members:



Private _dtPatient As DataTable

Private _daPatient As SqlDataAdapter

Private m_cn As SqlConnection

Public Event FillComplete()

Public Event FillIt()



Now, in a Method I call FillPatient is the actual guts of the process:

Try

_dtPatient.Clear()

_daPatient.Fill(_dtPatient)

RaiseEvent FillIt()

Catch excFill As SqlClient.SqlException

Console.WriteLine(excFill.Message)

End Try
 
J

John Saunders

Kueishiong Tu said:
I have a window form application. It requires to retrieve
data from a web site. Since the web request is very time
consuming, so I create a work thread to do the job.
How does the window main form (which is the main thread)
know when the work thread has finished the job so that
it can display the data received on the form? I do not
want to use a global variable since if the main form is
continuously checking for the global variable, it will
not be able to process the window UI events. I try to
raise an event when the work thread's job is done,
but how can the main thread detected this event?

When you raise an event from a particular thread, the event handling code is
executed on that same thread.

If you take a look at the Form.Invoke method in the documentation, you'll
see that it is able to invoke a delegate in the context of the form. So the
event handler in the form, running on the separate thread, can call
Form.Invoke, which will execute a delegate which will run in the main
thread, safely.
 
C

Christine Nguyen

Put the code that performs the work of the worker thread in a class member
subroutine or function. Declare the class in your form with the keyword
WithEvents. Raise an event in the class and then write an event handler in
your form that catches this class's event.

Public Class1
Public Event WorkDone()

Public Sub DoWork

' ....do work here....

RaiseEvent WorkDone
End Sub

End Class

In your form, declare the class WithEvents and catch the event by writing an
event handler.

Private WithEvents MyClass as Class1
Private Sub HandleTheEvent() Handles MyClass.WorkDone
'insert whatever code to handle the event
End Sub
 
J

John Saunders

Christine Nguyen said:
Put the code that performs the work of the worker thread in a class member
subroutine or function. Declare the class in your form with the keyword
WithEvents. Raise an event in the class and then write an event handler in
your form that catches this class's event.

Public Class1
Public Event WorkDone()

Public Sub DoWork

' ....do work here....

RaiseEvent WorkDone
End Sub

End Class

In your form, declare the class WithEvents and catch the event by writing an
event handler.

Private WithEvents MyClass as Class1
Private Sub HandleTheEvent() Handles MyClass.WorkDone
'insert whatever code to handle the event
End Sub

This won't work. "HandleTheEvent" will execute on the worker thread. You
need to use Form.Invoke to do anything to the form.
 
C

Christine Nguyen

That's true. The event will indeed be raised in the worker thread. The main
thread doesn't know per se that the worker thread is done, but depending on
what he requires, the main thread doesn't necessarily need to know. If he
just needs to update some stuff on the UI once the worker thread is
complete, for example, then he would just put that code in the event
handler. Control.Invoke would work also. There's more than one way to do
it, depending on what he wants to do exactly.
 
J

John Saunders

Christine Nguyen said:
That's true. The event will indeed be raised in the worker thread. The main
thread doesn't know per se that the worker thread is done, but depending on
what he requires, the main thread doesn't necessarily need to know. If he
just needs to update some stuff on the UI once the worker thread is
complete, for example, then he would just put that code in the event
handler. Control.Invoke would work also. There's more than one way to do
it, depending on what he wants to do exactly.

He cannot safely update the UI in the event handler. The only safe way to do
that is with Control.Invoke.

This is the sort of thing that can seem to be working, right up to the time
when it stops working.
 
C

Christine Nguyen

Good luck with what you're trying to do, Kueishiong. Threading can be
pretty complex and can definitely get "unsafe" as John pointed out. I don't
know what you're trying to do, but you would do best to try different things
out and really test thoroughly as threaded applications do get hairy and
unpredicatable.
 
J

John Saunders

Christine, you are giving out dangerous advice. In many cases, it is
impossible to reproduce threading problems on a development machine.
Many such problems will only happen rarely, and may only occur at a
debuggable rate on fast, multi-cpu machines.

Do you have some reason to believe that Control.Invoke would not be
necessary, even though the documentation says that it is necessary? Do
you actually believe that "trying various things" is an adequate way to
determine whether multithreaded code will work reliably?

John Saunders
(e-mail address removed)
 
C

Christine Nguyen

Hi John,

I think control.invoke would work in a pinch and that should be the
technique he favors if changing the UI is indeed what he wants to do,
although to be honest MSDN provides samples of changing the UI using the
other technique. Of course, I haven't always had the best luck with samples
from MSDN. Changing the UI is not specifically what he wants to do however.
It was just something I threw out there as an example of something he might
do. Therefore, control.invoke is not necessarily appropriate.

-Christine
 
J

John Saunders

Christine Nguyen said:
Hi John,

I think control.invoke would work in a pinch and that should be the
technique he favors if changing the UI is indeed what he wants to do,
although to be honest MSDN provides samples of changing the UI using the
other technique. Of course, I haven't always had the best luck with samples
from MSDN. Changing the UI is not specifically what he wants to do however.
It was just something I threw out there as an example of something he might
do. Therefore, control.invoke is not necessarily appropriate.

Actually, I believe he did talk about changing the UI.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top