【问题标题】:How can I use one BackgroundWorker to do different activities?如何使用一个 BackgroundWorker 进行不同的活动?
【发布时间】:2011-03-11 19:20:09
【问题描述】:

我正在使用 VB.NET 2005 进行编程。我正在使用BackgroundWorker 来加载大型列表或文本文件并对其进行处理。

是否可以使用相同的后台工作程序来处理其他内容,即处理已加载的文本文件?
有点像

Dim bwkMain as New BackgroundWorker()

如果可能的话,我如何以与第一个实现相同的形式实现它?

编辑
问题是:在完成一项任务后,是否可以将同一个 BackgroundWorker 用于另一项任务?

【问题讨论】:

  • 如果我没记错的话,您想使用一个 BackgroundWorker 来执行两个或多个不同的活动,对吧?

标签: .net vb.net backgroundworker


【解决方案1】:

可以让一个 BackgroundWorker 做两件或多件不同的事情。需要注意的是,如果您尝试让 BackgroundWorker 一次做不止一件事,因为它会导致您的代码失败。

这里简要概述了如何让 BackgroundWorker 执行多项活动。

  1. 检查 BackGroundWorker 是否正在处理某事。
    • 如果它已经在工作,您要么必须等待它完成,要么取消当前活动(这需要您在 DoWork 事件中采用不同的编码风格)。
    • 如果它不起作用,您可以安全地继续下一步。
  2. 使用参数(或参数)调用 BackgroundWorker 的 RunWorkerAsync 方法,该参数指定要做什么。
  3. 在 BackgroundWorker 的 DoWork 事件处理程序中,检查传递的参数 (e.Argument) 并执行所需的活动。

这里有一些示例代码可以指导您完成:

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

这是随之而来的形式:


还可以考虑阅读 MSDN 上的以下文章:



编辑

以上代码仅适用于 VB 10 (VS 2010)。为了在其他版本的 VB 中实现相同的代码,您必须编写大量代码,因为它们不支持匿名委托。

在旧版本的 VB 中,行

Public Sub Sample()
    Me.Invoke(Sub() txtResults.Text &= safeValue & vbCrLf)
End Sub

翻译成这样的:

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



按照以下步骤使代码在 VB 10 之前的版本中工作

添加此委托

Delegate Sub SetTextSafelyDelegate(ByVal text As String)

然后把所有的Me.Invoke(Sub() SetTextSafely(text))改成

Me.Invoke(New SetTextSafelyDelegate(AddressOf SetTextSafely), text)

另请注意,在我使用匿名委托设置文本的任何地方,您都必须重写代码以调用 SetTextSafely 方法。

例如,bgwWorker_DoWork 的 for 循环部分中的行 Me.Invoke(Sub() txtResults.Text &amp;= safeValue &amp; vbCrLf) 将变为 SetTextSafely(safeValue &amp; vbCrLf)



如果您想了解更多关于委托的信息,请阅读以下文章(均来自 MSDN)

【讨论】:

  • IDE 在Me.Invoke(Sub() SetTextSafely(text)) 中带下划线Sub() {表达式预期}
  • @Smith:抱歉耽搁了。我用您的问题的解决方案更新了答案。让我知道您是否需要了解其他信息。干杯。
【解决方案2】:

由于您不能并行运行两个任务,因此您可以像这样在同一个后台工作人员中按顺序执行它们

 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

【讨论】:

  • 我不想在第一个任务结束时立即运行第二个任务,我只想在用户单击按钮时运行它
  • @Smith 在这种情况下,让第二个 BackgroundWorker 运行会更干净。在用户单击时,您可以检查第一个是否由已完成事件或 IsBusy 标志完成,如果您确定第一个已完成,则可以触发第二个。
【解决方案3】:

非常模糊。启动另一个后台工作人员并让第一个工作人员等待它完成是没有意义的。如果您可以同时做一些事情,您只能从多个线程中受益。读取文件然后处理它是一个不能重叠的顺序操作。也许您可以同时进行一些处理,但从您的问题中无法猜测。

【讨论】:

  • 那么解决方案是什么,我使用 bwkMain 加载大文本文件,我可以使用相同的 bwkMain 处理文本文件,然后它有 @​​987654321@ 加载?
  • 是的,当然。我想不出你为什么认为这是不可能的。
  • 谢谢,你能帮我提供示例代码吗?请注意,要执行的两个任务是不同的。如何判断其中一项任务是否已完成,以及在bwrkMain_ProgressChangedbwrkMain_DoWork 事件中,如何根据已处理的任务执行操作?
猜你喜欢
  • 1970-01-01
  • 2017-01-04
  • 2023-03-20
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多