Search code examples
vb.netbackgroundworkerbackground-process

Stopping and then starting the same background worker in quick succession


I am having some difficulty dealing with stopping and then starting a background worker using VB.net when there is only a very small interval between the CancelAsync() call and the RunWorkerAsync() call. I have created an example to illustrate the problem below. The problem in this example occurs if the button is clicked once and then clicked twice in quick succession. Essentially, as I understand it, the problem is that the background worker is still iterating through the for loop (included to imitate code that takes some time to run) and has not yet had a chance to check to see if cancellation is pending.

The solution I attempted to use is to wait for the background worker to close and then start the worker again. However, this while loop iterates indefinitely - the background worker never terminates. I assume that putting the main thread to sleep also puts the background worker thread to sleep. Is this correct?

Question: what would be the recommended way of stopping a background worker and then potentially needing to start it again a fraction of a second later, when checking for a pending cancellation cannot reasonably be done any more often than it is?

Advice and comments much appreciated.

Public Class Form1
    Dim WithEvents bgWorker As System.ComponentModel.BackgroundWorker = New System.ComponentModel.BackgroundWorker
    Friend WithEvents startStopButton As Button = New Button
    Dim workerShouldBeRunning As Boolean = False
    Sub startStopButton_click() Handles startStopButton.Click
        If workerShouldBeRunning Then
            bgWorker.CancelAsync()
        Else
            While bgWorker.IsBusy
                Threading.Thread.Sleep(1)
            End While
            bgWorker.RunWorkerAsync()
        End If
        workerShouldBeRunning = Not workerShouldBeRunning
    End Sub
    Sub bgWorker_doWork() Handles bgWorker.DoWork
        While Not bgWorker.CancellationPending
            Dim sum As Long
            For counter = 1 To 100000
                sum += counter
            Next

            Threading.Thread.Sleep(1000)
        End While

    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        startStopButton.Location = New Point(10, 100)
        startStopButton.Size = New System.Drawing.Size(200, 23)
        startStopButton.Text = "Click" 'Click to start or stop the background dummy process.
        Me.Controls.Add(startStopButton)
        bgWorker.WorkerSupportsCancellation = True
    End Sub
End Class

Edit:

Using Ross Presser's advice, I have improved the example code. The solution seems a little poor, though.

Public Class Form1
    Dim WithEvents bgWorker As System.ComponentModel.BackgroundWorker = New System.ComponentModel.BackgroundWorker
    Friend WithEvents startStopButton As Button = New Button
    Dim workerShouldBeRunning As Boolean = False
    Dim startQueued As Boolean = False
    Sub startStopButton_click() Handles startStopButton.Click
        If workerShouldBeRunning Then
            bgWorker.CancelAsync()
        Else
            If bgWorker.IsBusy Then
                startQueued = True
            Else
                bgWorker.RunWorkerAsync()
            End If
        End If
        workerShouldBeRunning = Not workerShouldBeRunning
    End Sub
    Sub bgWorker_doWork() Handles bgWorker.DoWork
        While Not bgWorker.CancellationPending
            Dim sum As Long
            For counter = 1 To 100000
                sum += counter
            Next

            Threading.Thread.Sleep(1000)
        End While

    End Sub
    Sub bgWorkerCompleted() Handles bgWorker.RunWorkerCompleted
        If startQueued Then
            startQueued = False
            bgWorker.RunWorkerAsync()
        End If
    End Sub



    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        startStopButton.Location = New Point(10, 100)
        startStopButton.Size = New System.Drawing.Size(200, 23)
        startStopButton.Text = "Click" 'Click to start or stop the background dummy process.
        Me.Controls.Add(startStopButton)
        bgWorker.WorkerSupportsCancellation = True
    End Sub
End Class

Solution

  • How about this approach?...we simply replace the old backgroundworker with a new one:

    Public Class Form1
    
        Private WithEvents startStopButton As Button = New Button
        Private WithEvents bgWorker As System.ComponentModel.BackgroundWorker = Nothing
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            startStopButton.Location = New Point(10, 100)
            startStopButton.Size = New System.Drawing.Size(200, 23)
            startStopButton.Text = "Click" 'Click to start or stop the background dummy process.
            Me.Controls.Add(startStopButton)
        End Sub
    
        Private Sub startStopButton_click() Handles startStopButton.Click
            If IsNothing(bgWorker) Then
                Static bgwCounter As Integer = 0
                bgwCounter = bgwCounter + 1
                bgWorker = New System.ComponentModel.BackgroundWorker
                bgWorker.WorkerSupportsCancellation = True
                bgWorker.RunWorkerAsync(bgwCounter)
            Else
                bgWorker.CancelAsync()
                bgWorker = Nothing
            End If
        End Sub
    
        Private Sub bgWorker_doWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
            Dim bgwCounter As Integer = e.Argument
            Debug.Print("Running #" & bgwCounter)
            Dim bgw As System.ComponentModel.BackgroundWorker = sender
            While Not bgw.CancellationPending
                Dim sum As Long
                For counter As Integer = 1 To 100000
                    sum += counter
                Next
    
                Threading.Thread.Sleep(1000)
            End While
            Debug.Print("Cancelled #" & bgwCounter)
        End Sub
    
    End Class