Home All Groups Group Topic Archive Search About

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

Author
28 Nov 2006 2:36 AM
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

Author
28 Nov 2006 3:05 AM
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
J*@JL.com wrote:
Show quoteHide quote
> 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
Author
28 Nov 2006 3:32 AM
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.com wrote:
Show quoteHide quote
> 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
Author
29 Nov 2006 12:12 AM
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
Author
29 Nov 2006 12:43 AM
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.com wrote:
Show quoteHide quote
> 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
Author
30 Nov 2006 1:28 AM
JL
Thank you!!!!

This is what I needed!

On 28 Nov 2006 16:43:41 -0800, "FishingScout"
<fishingsc***@comcast.net> wrote:

Show quoteHide quote
>
>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.com wrote:
>> 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
Author
29 Nov 2006 2:13 AM
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

On Tue, 28 Nov 2006 18:12:43 -0600, J*@JL.com wrote:

Show quoteHide quote
>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)
Show quoteHide quote
>    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