Link to home
Start Free TrialLog in
Avatar of JedNebula
JedNebulaFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Multithreading question

Hi,

I am looking to spin up a new thread and wait for an amount of time before I kill it.

Is this possible to do whilst still allowing UI operations to update?

Below is a small example to show what I am struggling with:

Public Class Form1

    Sub HitMe()

        Do Until 1 = 2
            AppendMyText()
        Loop

    End Sub

    Private Sub btn_start(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnstart.Click
        Dim thr = New Threading.Thread(AddressOf HitMe)
        thr.IsBackground = True
        thr.Start()
        'thr.Join(10000) 'give it 10 seconds to run

    End Sub

    Sub AppendMyText()
        If Me.InvokeRequired Then
            Me.BeginInvoke(New MethodInvoker(Sub() AppendMyText()))
        Else
            Me.Textbox1.AppendText(Now.ToString & vbNewLine)
            Me.Textbox1.ScrollToCaret()
        End If
    End Sub

End Class

Open in new window


If I rem out the Thread.Join line, then the thread will be aborted after 10 seconds as I desire, but no textbox updates happen.

I would like to both have my cake and eat it if possible...
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

I suspect you just aren't giving the UI a chance to update because of the continual changing of the text - give something like the following a try.  It might need some adjustment with the DoEvents call.

Sub HitMe()

        Do Until 1 = 2
            AppendMyText()
DoEvents
        Loop

    End Sub
Avatar of JedNebula

ASKER

Hi Andy,

Thank you for the reply, but a DoEvents doesn't help because the UI thread is stuck waiting on the Join line.

Regards,

Rawden.
ASKER CERTIFIED SOLUTION
Avatar of Kimputer
Kimputer

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I did try that, but it didn't seem to quit the thread properly.

Used this example:
http://www.codeproject.com/Questions/528196/SettingplusTimeoutplusinplusvb-net

If someone could put a quick working example together I would be really grateful.
Before one gets too much in detail this looks to be a very artificial example.  Very likely the problem is what I said in my first comment.  

If that is the case then would your real requirement have the same problem?
(You do not have to 'join' threads to allow something to be calculated in one thread and have results displayed in the other.)
HI Andy,

I only tried using the thread.join due to the comments I found here:
http://stackoverflow.com/questions/321779/to-currentthread-abort-or-not-to-currentthread-abort

I did try your suggestion of the DoEvents in my test project, but it didn't make any difference. I can post it online if you like?
Avatar of Kimputer
Kimputer

Here's a working example:

Public Class Form1


    Sub AppendMyText()
        If Me.InvokeRequired Then
            Me.BeginInvoke(New MethodInvoker(Sub() AppendMyText()))
        Else
            Me.Textbox1.AppendText(Now.ToString & vbNewLine)
            Me.Textbox1.ScrollToCaret()
        End If
    End Sub


    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        BackgroundWorker1.CancelAsync()
        Timer1.Enabled = False
    End Sub

    Private Sub btnstart_Click(sender As Object, e As EventArgs) Handles btnstart.Click
        BackgroundWorker1.WorkerSupportsCancellation = True
        BackgroundWorker1.RunWorkerAsync()
        Timer1.Enabled = True
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork


        Dim i = 1
        While True
            i += 1
            If ((i Mod 1000) = 0) Then
                AppendMyText()
            End If
            If (BackgroundWorker1.CancellationPending) Then
                e.Cancel = True
                Exit While
            End If
            Application.DoEvents()
        End While

    End Sub


    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        If (e.Cancelled) Then
            MsgBox("Canceled")
        End If

    End Sub
End Class

Open in new window


Obviously you were overloading the application, so that's why I added the "If ((i Mod 1000) = 0) Then" to make it slightly more realistic (next time let the while loop be realistic as well as what's inside it..
OK, looked at that link - so why do you need the join
Thank you Kimputer. Just trying your example. Looks good initially. Thank you.

Andy, occasionally my worker routine was 'held up' by some process outside of my control. I need a way to cut it short after an amount of time so that the app can continue to run other tasks.

My real-world app runs some important tasks at 5 am in preparation for the working day when people arrive, but recently the app has been held up on one of the early running tasks and then I have to dial in and kill the errant process it is depending on.

I will spend some time later working out why the external resource was taking much longer than expected, but this question is about putting a fail-safe in place.
>>I need a way to cut it short after an amount of time so that the app can continue to run other tasks.

If a thread is running/stopped that should have no effect on the rest of the app.  If it does then it sounds like the interaction between the thread and the UI app is problematic.  (The thread forces the app to stop or the app is stopped to wait for the thread - either way is bad programming).
Although I am running a multi-threaded application, I still only fire off one task at a time. So if one takes longer than it should, the rest of the app doesn't hang - the UI still responds, but the app is waiting for that thread to finish before it can fire off another.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I don't think join is really the right method for me to use either. I only tried it because it recommended it in that article and I have to confess I only skim-read it. I was just looking for a thread.wait(x) method really, but that is what join seemed to do.

I think I have found a working solution now. I'll post it in a sec.
My working code:

Imports System.ComponentModel

Public Class Form1

    Private WithEvents tmr As Timer
    Private t As Threading.Thread

    Sub AppendMyText(text As String)
        If Me.Textbox1.IsDisposed Then Exit Sub
        If Me.Textbox1.InvokeRequired Then
            Me.Textbox1.BeginInvoke(New MethodInvoker(Sub() AppendMyText(text)))
        Else
            Me.Textbox1.AppendText(text & vbNewLine)
            Me.Textbox1.ScrollToCaret()
        End If
    End Sub

    Sub TheWork()
        Dim tc As Long
        Dim counter As Long
        While True

            tc = Environment.TickCount
            If tc Mod 1000 = 0 Then
                If tc <> counter Then
                    counter = tc
                    AppendMyText(Now.ToString)
                End If

            End If
            Application.DoEvents()
        End While

    End Sub
    Private Sub btnstart_Click(sender As Object, e As EventArgs) Handles btnstart.Click
        t = New Threading.Thread(AddressOf TheWork)
        t.IsBackground = True
        t.Start()
        tmr = New Timer
        tmr.Interval = 15000
        tmr.Enabled = True
    End Sub

    Private Sub tmr_Tick(sender As Object, e As EventArgs) Handles tmr.Tick
        tmr.Enabled = False
        If t IsNot Nothing Then
            t.Abort()
            t = Nothing
            Textbox1.Clear()
        End If
    End Sub
End Class

Open in new window