Good afternoon!
Can you tell me how to stop the execution of a thread? The following code doesn't work:
Private Sub Stop_Click(sender As Object, e As EventArgs) Handles Stop.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
That code does exactly what it is supposed to do, i.e. REQUEST a cancellation. It can't just stop working though, because it has no idea what work is being done. YOU are the one writing the code to do the work so YOU are the one who has to write the code to check whether a cancellation has been requested and to actually perform that cancellation. We have no idea what work you're doing so we have no idea where it is convenient to check whether a cancellation has been requested and what, if any, clean-up may be required in that case. Here's a basic example of what a cancellation might look like though:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Raise the DoWork event in a worker thread.
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
'This method is executed in a worker thread.
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
For i As Integer = 1 To 100
If worker.CancellationPending Then
'The user has cancelled the background operation.
e.Cancel = True
Exit For
End If
'Raise the ProgressChanged event in the UI thread.
worker.ReportProgress(i, i & " iterations complete")
'Perform some time-consuming operation here.
Threading.Thread.Sleep(250)
Next i
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.Label1.Text = TryCast(e.UserState, String)
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
'The background operation was cancelled.
Me.Label1.Text = "Operation cancelled"
Else
'The background operation completed normally.
Me.Label1.Text = "Operation complete"
End If
End Sub
Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles Button1.Click
'Only cancel the background opertion if there is a background operation in progress.
If Me.BackgroundWorker1.IsBusy Then
Me.BackgroundWorker1.CancelAsync()
End If
End Sub
Related
Hi so i have small form application
the problem was that when i was running tasks on the main thread, the program window wasn't responding
so my button redirects the task to the backgroudworker:
Private Sub btn_start_Click(sender As Object, e As EventArgs) Handles btn_start.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Now i want to output to my textbox (txtlog) some text from this Backgroudworker multiple time in one Sub. I found a solution that allows this. but this makes the code quite ugly if I have to use it several times:
Example
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If txtLog.InvokeRequired Then
txtLog.Invoke(Sub() txtLog.AppendText("My text" & vbNewLine))
txtLog.Invoke(Sub() txtLog.ScrollToCaret())
Else
txtLog.AppendText("My text" & vbNewLine)
txtLog.ScrollToCaret()
End If
Something in Backgroudworker . . .
If txtLog.InvokeRequired Then
txtLog.Invoke(Sub() txtLog.AppendText("My text" & vbNewLine))
txtLog.Invoke(Sub() txtLog.ScrollToCaret())
Else
txtLog.AppendText("My text" & vbNewLine)
txtLog.ScrollToCaret()
End If
. . .
End Sub
Is there any shorter method that would allow me to write in textbox?
The whole point of using a BackgroundWorker is so that you don't have to call Invoke and use delegates. If you're going to do that anyway then the BackgroundWorker is pointless.
With regards to the code you have, what's the point of testing InvokeRequired when you know for a fact that the DoWork event handler will be executed on a background thread? It's always going to be True. Also, why would you call Invoke twice and add all that overhead when you can call it once and do as many things as you like in the one call?
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
txtLog.Invoke(Sub()
txtLog.AppendText("My text" & vbNewLine))
txtLog.ScrollToCaret()
End Sub)
As I said though, you shouldn't be calling Invoke at all. If you want to do something on the UI thread from a BackgroundWorker then you call ReportProgress and handle the ProgressChanged event. The code you want executed goes in the event handler and you call ReportProgress as required. If you need to pass data, you do so using the second argument to ReportProgress and get it back from e.UserState in the event handler. Here's one I prepared earlier:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Raise the DoWork event in a worker thread.
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
'This method is executed in a worker thread.
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
For i As Integer = 1 To 100
'Raise the ProgressChanged event in the UI thread.
worker.ReportProgress(i, i & " iterations complete")
'Perform some time-consuming operation here.
Threading.Thread.Sleep(250)
Next i
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.Label1.Text = TryCast(e.UserState, String)
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Me.Label1.Text = "Operation complete"
End Sub
I have a background event that is consistently withdraws data. When I close the form background event doesn't cancel for some reason. If I enable a message in between it does...... I tried to put a time pause, REFRESH, and both. But its only works properly after the message box. I don't really understand why, since button it self to cancel works fine when form is not close. ERROR "Cannot access a disposed object.Object name: 'Form1'." Somehow it doesn't see "False" on closing.
Thanks!
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
RED(False)
'Some other functions - WORK OK
End Sub
Private Sub RED(ByVal reads As Boolean)
bck.WorkerSupportsCancellation = True
If reads = True Then
bck.RunWorkerAsync()
ElseIf reads = False Then
bck.CancelAsync()
bck.Dispose()
End If
End Sub
Private Sub bck_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bck.DoWork
Do
If bck.CancellationPending = True Then --- DOESN'T look like see this without Message BOX
Exit Sub
End If
Me.Invoke(Sub()
'EVENT HERE WERE I AM GETTING AN ERROR --- HOWEVER, Works as a button when called on RED(False) and Works if I put a message BOX
End Sub)
Loop
End Sub
It would appear to be a matter of timing, as is often the case with multiple threads. What I would suggest is that, if the form is closed when the background task is in progress, you cancel the background task and then cancel the close. You can then close again when the background task has completed, e.g.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
If BackgroundWorker1.CancellationPending Then
'Cancel the background work.
Exit Do
End If
'Do the background work.
'...
Loop
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Close the form.
Close()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If BackgroundWorker1.IsBusy Then
'Cancel the background task.
BackgroundWorker1.CancelAsync()
'Do not close the form this time.
e.Cancel = True
End If
End Sub
If you won't always want to close the form when the background task completes then you can indicate that to the RunWorkerCompleted event handler by, for instance, setting e.Result in the DoWork event handler and then testing it in the RunWorkerCompleted event handler, e.g.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
e.Result = False
Do
If BackgroundWorker1.CancellationPending Then
e.Result = True
'Cancel the background work.
Exit Do
End If
'Do the background work.
'...
Loop
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If CBool(e.Result) Then
'Close the form.
Close()
End If
End Sub
Use PROGRESS REPORT instead of Invoke.
Private Sub bck_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bck.DoWork
Do
If bck.CancellationPending = True Then
Exit Sub
End If
bck.ReportProgress("WHATEVER YOU CAPTURING/VALUES UPDATE")
Loop
End Sub
Private Sub bck_dProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bck.ProgressChanged
YOUR DISPLAY = ("WHATEVER YOU CAPTURING/VALUES UPDATE")
End Sub
I have a BackgroundWorker in my project that I want to restart in a single method. However, when I use the CancelAsync() method and then the RunWorkerAsync() method immediately after, the cancel hasn't completed and 2 instances of the BackgroundWorker are started and an InvalidOperationException is thrown. To combat this, I made the method async (it's a Sub method) and awaited the CancelAsync() to wait for it to finish. However, this returns an error that the expression doesn't produce a value. Is this due to the calling or the worker's method?
Worker Method:
Private Sub GIFworker(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bkwGIF.DoWork
Dim hum As Integer = e.Argument
If hum Mod 2 = 1 Then
While bkwGIF.CancellationPending = False
pbxRpsHum.Image = ilRPS.Images(0)
Thread.Sleep(1000)
pbxRpsHum.Image = ilRPS.Images(1)
Thread.Sleep(1000)
End While
Else
While bkwGIF.CancellationPending = False
pbxRpsHum.Image = ilRPS.Images(0)
Thread.Sleep(1000)
pbxRpsHum.Image = ilRPS.Images(3)
Thread.Sleep(1000)
End While
End If
End Sub
Calling CancelAsync() (hum is an integer):
Await bkwGIF.CancelAsync()
bkwGIF.RunWorkerAsync(hum)
Here's an example of how you cancel a BackgroundWorker:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Raise the DoWork event in a worker thread.
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
'This method is executed in a worker thread.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
For i As Integer = 1 To 100
If worker.CancellationPending Then
'The user has cancelled the background operation.
e.Cancel = True
Exit For
End If
'Raise the ProgressChanged event in the UI thread.
worker.ReportProgress(i, i & " iterations complete")
'Perform some time-consuming operation here.
Threading.Thread.Sleep(250)
Next i
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.Label1.Text = TryCast(e.UserState, String)
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
'The background operation was cancelled.
Me.Label1.Text = "Operation cancelled"
Else
'The background operation completed normally.
Me.Label1.Text = "Operation complete"
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Only cancel the background opertion if there is a background operation in progress.
If Me.BackgroundWorker1.IsBusy Then
Me.BackgroundWorker1.CancelAsync()
End If
End Sub
If you want to restart the background work on a cancellation then you would do so in the RunWorkerCompleted event handler when you detect that e.Cancelled is True.
I'm quite confused about progress bar in VB.net, here is my code
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ProgressBar_my.Minimum = 0
ProgressBar_my.Maximum = 10
ProgressBar_my.Step = 1
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProgressBar_my.PerformStep()
Threading.Thread.Sleep(5000)
If 1 = 1 Then
ProgressBar_my.PerformStep()
ProgressBar_my.PerformStep()
End If
'Threading.Thread.Sleep(2000)
End Sub
For the above code, what I expected is that after I click Button1, the progress bar will increase the progress status by 1, then it will pause for 5 sec, then it will increase the progress status by 2 at once.
However, after I ran the above code, what I saw was that after I click Button1, the progress bar will increase by 3 continually after 5 sec.
Can someone tell me why it behaves like this and How should I program my code so that I can increase by 1, then pause 5 sec and then increase by 2?
Thanks in advance!
I think what you are seeing (or not seeing) is the fact that the progress bar takes a finite amount of time to advance each step.
When you call Threading.Thread.Sleep on the UI thread this stops the progress bar from being redrawn until after the Sleep
What you should do is update the progress bar on a background worker instead, then I think you will see the effect you desire.
Add a BackgroundWorker to your form
Change your button click code to start the worker:
Private Sub frmSO_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ProgressBar_my.Minimum = 0
ProgressBar_my.Maximum = 10
ProgressBar_my.Step = 1
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync
End Sub
Then perform the update in the DoWork event:
'define a delegate to handle updates to the UI thread
Private Delegate Sub PerformStepCallback()
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim cb As PerformStepCallback = AddressOf ProgressBar_my.PerformStep
Me.BeginInvoke(cb)
Threading.Thread.Sleep(5000)
Me.BeginInvoke(cb)
Me.BeginInvoke(cb)
End Sub
The problem is that you call Threading.Thread.Sleep(5000), which will, well, pause the current thread. But that also means that the window won't be redrawn, so you see the effect of the first call to PerformStep only after the thread is no longer paused.
You can use another thread for wait and the second update; the easiest way in this case is using the Task Parallel Library:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProgressBar1.PerformStep()
Task.Delay(5000).ContinueWith(Sub()
ProgressBar1.PerformStep()
ProgressBar1.PerformStep()
End Sub, TaskScheduler.FromCurrentSynchronizationContext)
End Sub
Thanks Matt Wilko for giving me the clue. Here is my code.
From the Components tab of the Toolbox, add a BackgroundWorker component named backgroundWorker2. Create DoWork and ProgressChanged event handlers for the BackgroundWorker.
The following code will initiate the Component.
Public Sub New()
InitializeComponent()
BackgroundWorker2.WorkerReportsProgress = True
BackgroundWorker2.WorkerSupportsCancellation = True
End Sub
Call RunWorkerAsync when the button is clicked and the background worker is not running.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If BackgroundWorker2.IsBusy <> True And ProgressBar_my.Value < 6 Then
BackgroundWorker2.RunWorkerAsync()
End If
End Sub
Private Sub BackgroundWorker2_DoWork_1(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles BackgroundWorker2.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
worker.ReportProgress(1)
System.Threading.Thread.Sleep(2000)
worker.ReportProgress(1)
worker.ReportProgress(1)
End Sub
The following event is raised when every time the ReportProgress method is called.
Private Sub backgroundWorker2_ProgressChanged(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles BackgroundWorker2.ProgressChanged
ProgressBar_my.PerformStep()
End Sub
More information about the BackgroundWorker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
I am trying to build a log parser in VB.Net that will take IIS logs and insert them into a database. Having never really made a full out desktop application, I'm hitting a number of stumbling blocks, so please forgive me if my questions a very uninformed; I'm learning to walk while running.
The code that I'm working with looks like this:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim logfile = "C:\ex111124.log"
Dim FileLength As Long = New System.IO.FileInfo(logfile).Length
logFileLabel.Text = logfile
Dim objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
OngoingLog.AppendText(objReader.ReadLine)
'BackgroundWorker1.ReportProgress(e.percentProgress)
Loop()
objReader.Close()
objReader = Nothing
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'Me.crunchingProgress.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Close()
End Sub
So the function works, when this window is opened it starts to read the log file and updates a textbox with all of the rows currently read, but I also want it to update a progress bar in my main thread called crunchingProgress.
Any help would be greatly appreciated.
This should do the trick:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Invoke(Sub()
Me.crunchingProgress.Value = e.ProgressPercentage
End Sub)
End Sub
You don't set the BackgroundWorker to report progress (WorkerReportsProgress)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
Now your BackgroundWorker1_ProgressChanged will be called in the context of the UI Thread and you can set the progressbar value
Of course, when you call ReportProgress to raise the ProgressChanged event, you need to pass the percentage of work done so far.
Using objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
Dim line = objReader.ReadLine()
OngoingLog.AppendText(line)
Dim pct = Convert.ToInt32((100 * line.Length) / FileLength )
BackgroundWorker1.ReportProgress(pct)
Loop
End Using