Reputation: 5961
I am programming using VB.NET 2005. I am using a BackgroundWorker
to load a large list or text file and process it.
Is it possible to use the same background worker to process something else, i.e. to process the text file that was loaded?
somthing like
Dim bwkMain as New BackgroundWorker()
If its possible, how do I implement it in the same form as I have already implemented for the first one?
EDIT
The question is: is it possible to use the same BackgroundWorker for another task, after it finishes one task?
Upvotes: 1
Views: 6727
Reputation: 12613
It is possible to have a single BackgroundWorker do two or more different things. The caveat is that if you try to have the BackgroundWorker do more than one thing at a time as it will cause your code to fail.
Here is a brief overview of how to get the BackgroundWorker to do multiple activities.
DoWork
event).RunWorkerAsync
method with an argument (or parameter) which specifies what to do. DoWork
event handler, check the passed argument (e.Argument
) and do the desired activity.Here is some sample code to guide you through:
Public Class Form1
Public WithEvents bgwWorker1 As System.ComponentModel.BackgroundWorker
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
bgwWorker1 = New System.ComponentModel.BackgroundWorker
With bgwWorker1
.WorkerReportsProgress = True 'we'll need to report progress
.WorkerSupportsCancellation = True 'allows the user to stop the activity
End With
End Sub
Private Sub Form1_Disposed() Handles Me.Disposed
'you'll need to dispose the backgroundworker when the form closes.
bgwWorker1.Dispose()
End Sub
Private Sub btnStart_Click() Handles btnStart.Click
'check if the backgroundworker is doing something
Dim waitCount = 0
'wait 5 seconds for the background worker to be free
Do While bgwWorker1.IsBusy AndAlso waitCount <= 5
bgwWorker1.CancelAsync() 'tell the backgroundworker to stop
Threading.Thread.Sleep(1000) 'wait for 1 second
waitCount += 1
Loop
'ensure the worker has stopped else the code will fail
If bgwWorker1.IsBusy Then
MsgBox("The background worker could not be cancelled.")
Else
If optStep2.Checked Then
bgwWorker1.RunWorkerAsync(2)
ElseIf optStep3.Checked Then
bgwWorker1.RunWorkerAsync(3)
End If
btnStart.Enabled = False
btnStop.Enabled = True
End If
End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
'to stop the worker, send the cancel message
bgwWorker1.CancelAsync()
End Sub
Private Sub bgwWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwWorker1.DoWork
'get the value to be used in performing the steps
'in your case, you might have to convert it to a string or something
'and then do a Select Case on the result.
Dim stepValue = CInt(e.Argument)
'you cannot change the property of any control from a thread different
'from the one that created it (the UI Thread) so this code would fail.
'txtResults.Text &= "Starting count in steps of " & stepValue & vbCrLf
'to perform a thread-safe activity, use the ReportProgress method like so
bgwWorker1.ReportProgress(0, "Reported: Starting count in steps of " & stepValue & vbCrLf)
'or invoke it through an anonymous or named method
Me.Invoke(Sub() txtResults.Text &= "Invoked (anon): Starting count in steps of " & stepValue & vbCrLf)
SetTextSafely("Invoked (named): Starting count in steps of " & stepValue & vbCrLf)
For i = 0 To 1000 Step stepValue
'Visual Studio Warns: Using the iteration variable in a lambda expression may have unexpected results.
' Instead, create a local variable within the loop and assign it the value of
' the iteration variable.
Dim safeValue = i.ToString
Me.Invoke(Sub() txtResults.Text &= safeValue & vbCrLf)
'delibrately slow the thread
Threading.Thread.Sleep(300)
'check if there is a canellation pending
If bgwWorker1.CancellationPending Then
e.Cancel = True 'set this to true so we will know the activities were cancelled
Exit Sub
End If
Next
End Sub
Private Sub SetTextSafely(ByVal text As String)
If Me.InvokeRequired Then
Me.Invoke(Sub() SetTextSafely(text))
Else
txtResults.Text &= text
End If
End Sub
Private Sub bgwWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwWorker1.ProgressChanged
'everything done in this event handler is on the UI thread so it is thread safe
txtResults.Text &= e.UserState.ToString
End Sub
Private Sub bgwWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwWorker1.RunWorkerCompleted
'everything done in this event handler is on the UI thread so it is thread safe
If Not e.Cancelled Then
txtResults.Text &= "Activities have been completed."
Else
txtResults.Text &= "Activities were cancelled."
End If
btnStart.Enabled = True
btnStop.Enabled = False
End Sub
Private Sub txtResults_TextChanged() Handles txtResults.TextChanged
'place the caret at the end of the line and then scroll to it
'so that we always see what is happening.
txtResults.SelectionStart = txtResults.TextLength
txtResults.ScrollToCaret()
End Sub
End Class
And this is the form that goes with it:
Also consider reading the following articles on MSDN:
The code above works in VB 10 (VS 2010) only. In order to implement the same code in other versions of VB, you'll have to write a significant amount of code as they do not support anonymous delegates.
In older versions of VB, the line
Public Sub Sample()
Me.Invoke(Sub() txtResults.Text &= safeValue & vbCrLf)
End Sub
translates to something like this:
Public Delegate AnonymousMethodDelegate(value as String)
Public Sub AnonymousMethod(value as String)
txtResults.Text &= value
End Sub
Public Sub Sample()
Me.Invoke(New AnonymousMethodDelegate(AddressOf AnonymousMethod), safeValue & vbCrLf)
End Sub
Follow these steps to get the code to work in pre VB 10
Add this delegate
Delegate Sub SetTextSafelyDelegate(ByVal text As String)
And then change all the Me.Invoke(Sub() SetTextSafely(text))
to
Me.Invoke(New SetTextSafelyDelegate(AddressOf SetTextSafely), text)
Also note that anywhere that I set the text with an anonymous delegate, you'll have to rewrite the code to call the SetTextSafely
method.
For instance, the line Me.Invoke(Sub() txtResults.Text &= safeValue & vbCrLf)
in the for loop section of the bgwWorker_DoWork
will become SetTextSafely(safeValue & vbCrLf)
If you'd like to know more about delegates, read the following articles (all from MSDN)
Upvotes: 7
Reputation: 941465
Very vague. It never makes sense to start another background worker and have the first one wait for it to complete. You only get a benefit from multiple threads if you can do stuff at the same time. Reading a file then processing it is a sequential operation that cannot be overlapped. Maybe you can do some of the processing concurrently, but that's unguessable from your question.
Upvotes: 1
Reputation: 108957
Since you cannot run your two tasks in parallel, you could do them sequentially in the same backgroundworker like this
BackgroundWorker1.RunWorkerAsync(args)
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles BackgroundWorker1.DoWork
DoTask1() ' Read files.
DoTask2() ' Process data that was read.
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles BackgroundWorker1.RunWorkerCompleted
'Tasks Done
End Sub
Upvotes: 2