Как я могу использовать один BackgroundWorker для различных действий? - PullRequest
1 голос
/ 11 марта 2011

Я программирую с использованием VB.NET 2005. Я использую BackgroundWorker, чтобы загрузить большой список или текстовый файл и обработать его.

Можно ли использовать тот же фоновый рабочий для обработки чего-то другого, то есть для обработки загруженного текстового файла?
что-то вроде

Dim bwkMain as New BackgroundWorker()

Если это возможно, как мне реализовать его в той же форме, в которой я уже реализовал первый?

EDIT
Вопрос: можно ли использовать тот же BackgroundWorker для другой задачи, после того, как он завершит одну задачу?

Ответы [ 3 ]

7 голосов
/ 12 марта 2011

Возможно, чтобы один BackgroundWorker делал две или более разные вещи.Предостережение заключается в том, что если вы попытаетесь заставить BackgroundWorker выполнять несколько действий одновременно, так как это приведет к сбою вашего кода.

Вот краткий обзор того, как заставить BackgroundWorker выполнять несколько действий.

  1. Проверьте, не работает ли BackGroundWorker на чем-либо.
    • Если он уже работает, вам нужно будет либо дождаться его завершения, либо отменить текущее действие (что потребует применения другого стиля кодирования в событии DoWork).
    • Если он не работает, вы можете перейти к следующему шагу.
  2. Вызвать метод BackgroundWorker's RunWorkerAsync с аргументом (или параметром), который указывает, что нужноделать.
  3. В обработчике событий DoWork BackgroundWorker's проверьте переданный аргумент (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

И вот форма, которая идет с ним:
Image of Form Layout for

Также рассмотрите возможность прочтения следующих статей по 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.

Например, строка Me.Invoke(Sub() txtResults.Text &= safeValue & vbCrLf) в разделе цикла цикла forbgwWorker_DoWork станет SetTextSafely(safeValue & vbCrLf)

Если вы хотите узнать больше о делегатах, прочитайте следующие статьи (все из MSDN)

2 голосов
/ 11 марта 2011

Поскольку вы не можете запускать две задачи параллельно, вы можете выполнять их последовательно в том же фоновом редакторе, как этот

 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
1 голос
/ 11 марта 2011

Очень расплывчато.Никогда не имеет смысла запускать другого фонового работника и заставлять первого ждать его завершения.Вы можете получить выгоду от нескольких потоков, если вы можете делать вещи одновременно.Чтение файла с последующей обработкой - это последовательная операция, которая не может перекрываться.Может быть, вы можете выполнить некоторую обработку одновременно, но это не понятно из вашего вопроса.

...