Newbie help: How to detect when a worker thread ihas completed normaly

J

JL

for VB 2005

On the main UI several controls are disabled and sevral variables are
set BEFORE the background thread is started

I would like to renabled those controls and reset those variables
after the background thread is finished.

I could see using the control.invoke. but with the number of controls
and variables I would just like to call a seperate sub that renables
the controls like the one that disables them

Thread.join (as I understand it) won't work as I do not want to lock
the form.
Events won't (I could be wrong) because they are raised on the same
background thread (again the UI controls)

I'm thinking I must use a timer control to check if the background
thread is still running.

If that works then fine
But is there there another way?

Bottom line: when a worker thread started by the UI is done how can I
trigger a procedure in the UI?


Thank you
 
E

eric.burgin

JL, it depends on how you are doing the threading. Here is some code
that will work using the new BackgroundWorker object:

Imports system.ComponentModel 'This is where BackgroundWorker is
Public Class Form1

'Make sure you Dim WithEvents
Dim WithEvents myBackgroundWorker As BackgroundWorker


Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load

'Disable the controls that we will enable later
btnMain.Enabled = False

End Sub

Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnGo.Click

'Create a new instance
myBackgroundWorker = New BackgroundWorker
' Start the thread. This fires the Do_Work event (see below)
myBackgroundWorker.RunWorkerAsync()

End Sub


Private Sub myBackgroundWorker_DoWork(ByVal sender As Object, ByVal
e As System.ComponentModel.DoWorkEventArgs) Handles
myBackgroundWorker.DoWork


'This is where the code goes for your worker thread to do all
its work

End Sub


Private Sub myBackgroundWorker_RunWorkerCompleted(ByVal sender As
Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
Handles myBackgroundWorker.RunWorkerCompleted
'This event fires when the Worker is completed so enable the
controls
btnMain.enabled = True
End Sub
End Class



Does that help?
Eric B
 
F

FishingScout

Create a windows application project and add one button to Form1,
leaving the name of the button as button1. Then replace the Form1.vb
with the code below.

I think this will do what you are looking for. Clicking the button
will start a thread. Inside the thread a call is made to a method of
the Form, in this case it simply opens a msgbox. After a 3 second
delay, simulating some work, the thread calls a second method in the
form, passing a single string parameter which again simply opens a
msgbox.


Imports System.Threading

Public Class Form1

Private Shared GUI As Form1 = Nothing

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
GUI = Me
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

Dim Button1ClickThread As New Thread(AddressOf ThreadFunction)

Button1ClickThread.Start()
End Sub

Private Delegate Sub ThreadStartedDelegate()
Private Sub ThreadStarted()

'Do some thread started gui work here
MsgBox("Thread Started")

End Sub

Private Delegate Sub ThreadStoppedDelegate(ByVal Parameter As
String)
Private Sub ThreadStopped(ByVal Parameter As String)

'Do some thread stopped gui work here
MsgBox("Thread Stopped [" + Parameter + "]")

End Sub

Private Sub ThreadFunction()

Dim ThreadStartedFunction As New
ThreadStartedDelegate(AddressOf GUI.ThreadStarted)
GUI.Invoke(ThreadStartedFunction)

Thread.Sleep(3000)

Dim ThreadStoppedFunction As New
ThreadStoppedDelegate(AddressOf ThreadStopped)
GUI.Invoke(ThreadStoppedFunction, "Feels good")

End Sub

End Class
 
J

JL

Thank you both for taking the time to respond.
I should have posted my the code so so you two could see where I'm
coming from, sorry about that.

Some background on the actual program I wrote and want to change:
I already wrote( for personal use) a program that uses the background
component. While this proram runs fine I want better control/options
of the background thread so I want to use the system.thread way of
doing it.

Most of the processing are done in several modules which may be used
seperately or done one after the other though only one background run
at a time. All of these modules have programs that will display
information in the main UI while running.

The "testbutton" only purpose is to represent several controls (from
the program mentioned above) that are disabled and must be reenabled
after the background thread ends.

So I wrote this test project to help me undertstand delegates and
control.invoke updates. It is written to basically emulate how the
actual program works when it comes to updating the controls. I used
someone else's code on the internet, I removed the extra stuff and
made some changes so it reflects how I need to update the textbox.

Like the main program this code will:
1) start a background process at the button click.
The sub that is run is in a module where it must stay.
2) As the thread runs the sub will update the main form by
* sending the text to be displayed
*sending a control code (represented initially by the ctr
variable)
*pausing the worker thread. Pausing takes place in the worker
thread and the pause duration will vary

3) repeat til done


Now that the updating part works, what I need now is to alert the main
UI that the thread is completed so the main UI does the reenabling of
the controls.

So after the thread that runs the "startprocess" is completed, how do
I trigger the main UI to call the enablebuttons() sub?

I see only doing a series of control.invokes if the only way is to
have the background thread be the trigger.

Please let me know if both your sample codes will still work now that
I presented the test code.

Thank you both very much for helping.

------------------------------------------------

Imports System.Threading
Public Class Form1
Dim xctr As Integer
Private Delegate Sub UpDateGSLDelegate(ByVal txt As String, ByVal
rpn As Integer)

Public Sub updateTxtBox(ByVal txt As String, ByVal rpn As Integer)
If txtBoxtest.InvokeRequired Then
Invoke(New UpDateGSLDelegate(AddressOf UpDateTheGSL), txt,
rpn)
Else
UpDateTheGSL(txt, rpn)
End If
End Sub

Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnStart.Click
Dim t1 As New System.Threading.Thread(AddressOf startproces)
testbutton.Enabled = False
t1.Start()
End Sub

Private Sub enablebuttons()
testbuttont.Enabled = True
End Sub
Public Sub UpDateTheGSL(ByVal txt As String, ByVal rpn As Integer)
txtBoxtest.Text = txt & rpn
End Sub

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As
System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
MainForm = Nothing
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
MainForm = Me
End Sub
End Class

----------------------------------------------
Module Module2
Public MainForm As Form1

Public Sub startProcess()
For ctr As Integer = 0 To 5
UpdateMainForm("kkjkjkjk", ctr, 1000)
Next
End Sub

Sub UpdateMainForm(ByVal txt As String, ByVal rpn As Integer,
ByVal xsleep As Integer)
MainForm.updateTxtBox(txt, rpn)
System.Threading.Thread.Sleep(xsleep)
End Sub
End Module
 
F

FishingScout

The example I provided contains a thread that calls a method of the
main UI when it stops. In the example, the thread will call the Form1
sub routine named "ThreadStopped". The ThreadStopped sub routine can
freely interact with the Form and therefore can easily enable controls.
It makes no difference where the thread function resides. If the
thread is in another module then you would need to reference the GUI
object with "Form1.GUI" rather than just GUI. Here is the original
call made at the end of the thread's execution:

Dim ThreadStoppedFunction As New
ThreadStoppedDelegate(AddressOf ThreadStopped)
GUI.Invoke(ThreadStoppedFunction, "Feels good")

If the thread were running in another module, this code would need to
look like:

Dim ThreadStoppedFunction As New
ThreadStoppedDelegate(AddressOf Form1.GUI.ThreadStopped)
Fomr1.GUI.Invoke(ThreadStoppedFunction, "Feels good")


If you are starting multiple threads and you need to know when they all
have completed, then you will need to more work, such as implementing a
semaphore or use events and WaitAll (as examples).
 
J

JL

HOLD THE PHONE!!
I thought of this after I posted the below post!

I tried something and it works.
I added the code below with the asterisks to make the added code stand
out.

it does what I need, the background thread will simply send another
string with a specific value at the end and terminate as normal
and the mainform UI txtBoxtest_TextChanged event does the rest
independently.

Is there a way to create a raise event that does the same thing in the
same matter more or less?

Again, thank you both

Thank you both for taking the time to respond.
I should have posted my the code so so you two could see where I'm
coming from, sorry about that.

Some background on the actual program I wrote and want to change:
I already wrote( for personal use) a program that uses the background
component. While this proram runs fine I want better control/options
of the background thread so I want to use the system.thread way of
doing it.

Most of the processing are done in several modules which may be used
seperately or done one after the other though only one background run
at a time. All of these modules have programs that will display
information in the main UI while running.

The "testbutton" only purpose is to represent several controls (from
the program mentioned above) that are disabled and must be reenabled
after the background thread ends.

So I wrote this test project to help me undertstand delegates and
control.invoke updates. It is written to basically emulate how the
actual program works when it comes to updating the controls. I used
someone else's code on the internet, I removed the extra stuff and
made some changes so it reflects how I need to update the textbox.

Like the main program this code will:
1) start a background process at the button click.
The sub that is run is in a module where it must stay.
2) As the thread runs the sub will update the main form by
* sending the text to be displayed
*sending a control code (represented initially by the ctr
variable)
*pausing the worker thread. Pausing takes place in the worker
thread and the pause duration will vary

3) repeat til done


Now that the updating part works, what I need now is to alert the main
UI that the thread is completed so the main UI does the reenabling of
the controls.

So after the thread that runs the "startprocess" is completed, how do
I trigger the main UI to call the enablebuttons() sub?

I see only doing a series of control.invokes if the only way is to
have the background thread be the trigger.

Please let me know if both your sample codes will still work now that
I presented the test code.

Thank you both very much for helping.

------------------------------------------------

Imports System.Threading
Public Class Form1
Dim xctr As Integer
Private Delegate Sub UpDateGSLDelegate(ByVal txt As String, ByVal
rpn As Integer)

Public Sub updateTxtBox(ByVal txt As String, ByVal rpn As Integer)
If txtBoxtest.InvokeRequired Then
Invoke(New UpDateGSLDelegate(AddressOf UpDateTheGSL), txt,
rpn)
Else
UpDateTheGSL(txt, rpn)
End If
End Sub

Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnStart.Click
Dim t1 As New System.Threading.Thread(AddressOf startproces)
testbutton.Enabled = False
t1.Start()
End Sub

Private Sub enablebuttons()
testbuttont.Enabled = True
End Sub
Public Sub UpDateTheGSL(ByVal txt As String, ByVal rpn As Integer)
txtBoxtest.Text = txt & rpn
End Sub

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As
System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
MainForm = Nothing
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
MainForm = Me
End Sub

*** Private Sub txtBoxtest_TextChanged(ByVal sender As Object, ByVal e
As System.EventArgs) Handles txtBoxtest.TextChanged
If txtBoxtest.Text = "" Then
Me.enablebuttons()
End If
End Sub
End Class

----------------------------------------------
Module Module2
Public MainForm As Form1

Public Sub startProcess()
For ctr As Integer = 0 To 5
UpdateMainForm("kkjkjkjk", ctr, 1000)
Nex
**** UpdateMainForm("", 0, 10)
 
J

JL

Thank you!!!!

This is what I needed!

The example I provided contains a thread that calls a method of the
main UI when it stops. In the example, the thread will call the Form1
sub routine named "ThreadStopped". The ThreadStopped sub routine can
freely interact with the Form and therefore can easily enable controls.
It makes no difference where the thread function resides. If the
thread is in another module then you would need to reference the GUI
object with "Form1.GUI" rather than just GUI. Here is the original
call made at the end of the thread's execution:

Dim ThreadStoppedFunction As New
ThreadStoppedDelegate(AddressOf ThreadStopped)
GUI.Invoke(ThreadStoppedFunction, "Feels good")

If the thread were running in another module, this code would need to
look like:

Dim ThreadStoppedFunction As New
ThreadStoppedDelegate(AddressOf Form1.GUI.ThreadStopped)
Fomr1.GUI.Invoke(ThreadStoppedFunction, "Feels good")


If you are starting multiple threads and you need to know when they all
have completed, then you will need to more work, such as implementing a
semaphore or use events and WaitAll (as examples).
 

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