I'm having an issue where BackgroundWorker.CancelAsync() isn't working. I have WorkerSupportsCancellation set to TRUE. I am also polling BackgroundWorker1.CancellationPending in DoWork. Here is sample code of what I am trying to achieve. I have background worker looping through a time stamp and assigning a value to Measurement variable. I have a subroutine that queries the last reported Measurement variable and writes to listbox. After 5 loops I send BackgroundWorker.CancelAsync(). I can see the cancellation is pending, but it doesn't actually cancel the background worker. Is this a race condition?
Public Class Form1
Dim Measurement As String
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
Delay(0.5)
For x = 1 To 5
ListBox1.Items.Add(Measurement)
ListBox1.TopIndex = ListBox1.Items.Count - 1
TextBox1.Text = BackgroundWorker1.CancellationPending
Delay(1)
Next
BackgroundWorker1.CancelAsync()
TextBox1.Text = BackgroundWorker1.CancellationPending
ListBox1.Items.Add("Cancel Pend: " & BackgroundWorker1.CancellationPending)
Delay(5)
ListBox1.Items.Add("Busy: " & BackgroundWorker1.IsBusy)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Sub
Else
Do
Measurement = Now()
Loop
End If
End Sub
End Class
You just need to move the check for the cancellation inside the Do...Loop otherwise it will be tested only at the start of the DoWork event handler and never after that
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Else
Do
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Do
Else
Measurement = Now()
End If
Loop
End if
End Sub
Related
I am using VB.NET with Visual Studio 2017.
I am testing a form which has a datagridview. That grid is being populated with initial trading symbols. After loading symbols, I need to press Start button, which starts a timer1 to look for latest price of that symbol from exchange and start timer2. Timer2 will look for certain conditions of price, if met, will place an order to exchange and again look for another loop. While this is happening, timer1 is also running at 2 seconds interval to get latest trading price.
I am using two different BackgroundWorker for each timer to do these two tasks with grid.
This works almost fine, but sometimes, both or one of timers gets disabled.
I am looking for another good and best approach to this as far as code, design and architechure is concerned. If anyone can help.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.Timer1.Stop()
Try
With Me.DataGridView4
For RowIndex = 0 To .RowCount - 1
With .Rows(RowIndex)
'CODE FOR CHECKING LTP OF SYMBOL
End With
Next
End With
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker2_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker2.DoWork
Me.Timer2.Stop()
Try
For Each row As DataGridViewRow In Me.DataGridView4.Rows
Dim TRDSYM As String = row.Cells(1).Value
Dim orderid As String = row.Cells(6).Value
Dim Ltp As Decimal = row.Cells(2).Value
Dim EntryPrice As Decimal = Ltp
'PLACING AN ORDER TO EXCHANGE BASED ON CONDITION
Next
Catch ex As Exception
End Try
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'SetLog("Market Watch For Ltp Background Worker going on...", LogType.Others)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
'SetLog("Order Placement Algorithm Background Worker going on...", LogType.Others)
BackgroundWorker2.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Me.Timer2.Enabled = False Then
Timer2.Enabled = True
Me.Timer2.Start()
Me.btnOrderplacementstatus.Text = "Order Placement Running..."
Me.btnOrderplacementstatus.BackColor = Color.Green
End If
'SetLog("BackgroundWorker 2 Order Placement Thread started...", LogType.Others)
Me.Timer1.Enabled = True
Me.Timer1.Start()
Me.btnMarketwatchstatus.Text = "Market Watch Running..."
Me.btnMarketwatchstatus.BackColor = Color.Green
End If
End Sub
Private Sub BackgroundWorker2_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker2.RunWorkerCompleted
If Me.Timer2.Enabled = False Then
Me.Timer2.Enabled = True
Me.Timer2.Start()
Me.btnOrderplacementstatus.Text = "Order Placement Running..."
Me.btnOrderplacementstatus.BackColor = Color.Green
End If
End Sub
I have a form that prompts a user for confirmation before running a BackgroundWorker that performs some calculations. Those calculations can take anywhere from 10-30 seconds to run and I want to make sure that once the calculations begin running, they are allowed to finish uninterrupted.
Is there a way to temporarily disable the Close Button in the title bar until the BackgroundWorker finishes its job?
I found a couple similar questions but they look like a more permanent solution (here and here). I'd like the Close Button to be disabled only temporarily while the BackgroundWorker does its job.
Any help would be much appreciated. Thanks!
Private ImBusy As Boolean = False
Private Sub LookBusyForTheBoss()
Me.UseWaitCursor = True
Me.Cursor = Cursors.WaitCursor
Me.Enabled = False
ProgressBar1.UseWaitCursor = False
ProgressBar1.Style = ProgressBarStyle.Marquee
ImBusy = True
End Sub
Private Sub Form77_FormClosing(...) Handles Me.FormClosing
If ImBusy Then e.Cancel = True
End Sub
Private Sub OkHeIsGone()
Me.UseWaitCursor = False
Me.Cursor = Cursors.Default
Me.Enabled = True
ImBusy = False
End Sub
Disabling the close button would be bad taste in the extreme. Fix your application so that the calculation can be interrupted.
I agree with not hiding the 'X' but if you want it really bad I think this over here is what you are looking for:
Disable Close Button in Vb.Net
Something along these lines might work:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
doNotClose = True
Do
'
'etc
'
'simulate long running
For x As Integer = 1 To 1000
Threading.Thread.Sleep(10)
Next
Exit Do
Loop
doNotClose = False
If closerequested Then
Me.BeginInvoke(Sub()
Me.Close()
End Sub)
End If
End Sub
Dim doNotClose As Boolean = False
Dim closerequested As Boolean = False
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
closerequested = True
If doNotClose Then
e.Cancel = True
Exit Sub
End If
End Sub
or this
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
doNotClose = True
Do
'
'etc
'
'simulate long running
For x As Integer = 1 To 1000
Threading.Thread.Sleep(10)
Next
Exit Do
Loop
doNotClose = False
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If closerequested Then
Me.Close()
End If
End Sub
Dim doNotClose As Boolean = False
Dim closerequested As Boolean = False
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
closerequested = True
If doNotClose Then
e.Cancel = True
Exit Sub
End If
End Sub
I want to create a button that could stop my background worker and end all the process it is working on.
Here is my sample backgroundworker code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If BackgroundWorker1.IsBusy <> True Then
BackgroundWorker1.RunWorkerAsync()
End If
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim counter As Integer = 1
Do
'updated code with stop function----------------
BackgroundWorker1.WorkerSupportsCancellation = True
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
ProgressBar1.Value = 0
Exit Do
End If
'updated code with stop function----------------
ListBox1.Items.Add(counter)
ProgressBar1.Value = ((counter - 1) / limit) * 100
counter = counter + 1
Loop While(counter <= 999999999999999999)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Try
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_Completed(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Try
Catch ex As Exception
End Try
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
End Sub
'updated code with stop function----------------
Private Sub StopButton_Click(sender As Object, e As EventArgs) Handles StopButton.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
'updated code with stop function----------------
I want to reset the loop and return the Progress Bar to 0% when i stop the backgroundworker.
Is this possible?
The code above has been updated and it is now working fine.
I have added this code inside my do loop:
BackgroundWorker1.WorkerSupportsCancellation = True
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
ProgressBar1.Value = 0
Exit Do
End If
I created a button that stops the worker:
Private Sub StopButton_Click(sender As Object, e As EventArgs) Handles StopButton.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
The Backgroundworker class has the method CancelAsync() which you need to call to cancel the execution of the bgw.
You need to set the Backgroundworker.WorkerSupportsCancellation property to true and inside the while loop you need to check the CancellationPending property wether the value is true which indicates a call to the CancelAsync() method.
If CancellationPending evaluates to true, you would ( which you should have done already ) call one of the overloaded ReportProgress() (Docu) methods to set your ProgressBar value to the desired value.
EDIT: You should set the Cancel property of the DoWorkEventArgs to true so you can check the Cancelled property of the RunWorkerCompletedEventArgs inside the RunworkerCompletedevent.
You also shouldn not access any controls which lives in the UI thread. You better use the ProgressChanged(Docu) event.
See: BackgroundWorker Docu
Public Class Form1
Private iVal As Integer = 0
Private Sub bgw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
For iVal = iVal To 100 Step 1
bgw.ReportProgress(iVal)
Threading.Thread.Sleep(99)
If (bgw.CancellationPending = True) Then
e.Cancel = True
Exit For
End If
Next
End Sub
Private Sub bgw_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgw.ProgressChanged
pbar.Value = e.ProgressPercentage
lblProgrss.Text = e.ProgressPercentage.ToString() & "%"
End Sub
Private Sub bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
If (e.Cancelled = True) Then
pic.Visible = False
pbar.Value = iVal
lblProgrss.Text = iVal & "%"
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
Else
pic.Visible = False
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
iVal = 0
End If
End Sub
Private Sub btnstart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnstart.Click
If (btnstart.Text = "Start") Then
btnstart.Text = "Stop"
btnstart.BackColor = Color.Red
pic.Visible = True
bgw.RunWorkerAsync()
Else
If (bgw.IsBusy = True) Then
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
bgw.CancelAsync()
End If
End If
End Sub
End Class
The progress bar is repeated two or three times before completing the operation on my backgroundworker
this code:
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Load Data
For i As Integer = 0 To ListData.Count
BackgroundWorker1.ReportProgress((i / ListData.Count) * 100)
Next
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
End If
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'set datasource of DatagridView
ProgressBar1.Style = ProgressBarStyle.Blocks
End Sub
In my load form
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.RunWorkerAsync()
ProgressBar1.Style = ProgressBarStyle.Marquee
please help me
You have several mistakes. First, if you intend to display an increasing progress bar, you should use:
ProgressBar1.Style = ProgressBarStyle.Continuous
in your load form. Next, you are checking BackgroundWorker1.CancellationPending only after going through all your ListData. That is too late, you have to check it every iteration of the loop. I also really doubt you want your loop to go from 0 to ListData.Count; you probably either want to start at 1 or go to ListData.Count - 1. I can't tell from your question. Your loop should look more like this:
For i as Integer = 0 To ListData.Count - 1
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Exit For
Else
' You should be doing some work here, not just calling ReportProgress
BackgroundWorker1.ReportProgress(100 * (i+1) / ListData.Count)
End If
Next
Another mistake is calculating (i / ListData.Count) * 100; i and ListData.Count are integers, so their division will always be zero until the end, when it will be 1. Instead, multiply the numerator by 100 to get percentages.
I am using VB.NET and I have:
a Start button
a Stop button
a While loop
When the Start button is pressed, the While loop begins. I want to stop the loop when the Stop button is pressed.
I had tried to use Applications.DoEvents, but the loop stops when the Stop button is pressed twice.
Below is my code using Applications.DoEvents
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
While
' Perform the while statements
If stopclick = True Then
stopclick = False
Application.DoEvents()
Exit While
End If
End While
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub
Have I used Application.DoEvents incorrectly?
What are the other options besides Application.DoEvents? How can I stop the loop with a single Stop button click?
You need to put the call to Application.DoEvents outside of your If statement, like this:
While True
Application.DoEvents()
' Perform the while statements
If stopclick Then
stopclick = False
Exit While
End If
End While
The Stop_Click won't have a chance to be processed until you call DoEvents, so, with the way you had it, stopClick would never get set to True.
However, the larger issue, here, is that calling DoEvents, at all, is very bad practice and can lead to some very difficult-to-fix bugs if you aren't very careful. It would be far better if the loop was performed in a background thread. Check out the BackgroundWorker component for an easy to implement threading mechanism for WinForm projects. You'll find it in the tool box of your form designer.
Here's an example of how to do it with the BackgroundWorker component:
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
stopClick = False
While Not stopClick
' Perform the while statements
End While
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
btnForward.Enabled = True
btnStop.Enabled = False
End Sub
You can simplify your while loop significantly by using stopClick as your condition:
While Not stopClick
Application.DoEvents()
End While
I know this is an old thread, but I found a simple workaround. Add a CheckBox to the form (it can even be "off the side of the form," if that made sense).
Private Sub btnStopLoop_Click(sender As System.Object, e As System.EventArgs) Handles btnStopLoop.Click
chkEndLoop.CheckState = CheckState.Checked
End Sub
Then, in the Loop you wish to exit:
Do Until chkEndLoop.CheckState = CheckState.Checked
'Do Stuff Here
Loop
As long as the CheckBox.CheckState = CheckState.Unchecked, the Loop will continue, but when the CheckBox becomes Checked, the Loop will Exit
You have to use btnstop.focus() at the start of the loop. This will be sure to solve your problem.
Example:
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
btnStop.Focus() ' new code---------------------
While
' Perform the while statements
If stopclick = True Then
stopclick = False
Application.DoEvents()
Exit While
End If
End While
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub