【问题标题】:How to wait for BackgroundWorker to finish without killing ProgressBar?如何等待 BackgroundWorker 完成而不杀死 ProgressBar?
【发布时间】:2016-06-19 01:49:20
【问题描述】:

该应用程序的功能远不止于此,但我已通过以下示例缩小了问题范围。

当bgwDone.WaitOne()被注释掉时,进度条工作正常,取消按钮有效,但在后台进程完成之前继续执行。

在应用bgwDone.WaitOne()时,ProgressForm是可见的,但没有启用,所以不能取消处理,进度条不刷新,而且最混乱的部分,Msgbox("1") 不执行。我只在后台工作人员完成后看到 Msgbox("2") 。我完全迷惑了。

Imports System.ComponentModel

Public Class Form1
    Private WithEvents bgw As BackgroundWorker
    Private Event bgwCancelled()
    Private bgwDone As New System.Threading.AutoResetEvent(False)

    'Allows ProgressForm to cancel execution
    Public Sub bgwCancelAsync()
        RaiseEvent bgwCancelled()
    End Sub

    Private Sub bgw_Cancelled_by_ProgressForm() Handles Me.bgwCancelled
        bgw.CancelAsync()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Cursor = Cursors.WaitCursor
        bgw = New BackgroundWorker
        bgw.WorkerReportsProgress = True
        bgw.WorkerSupportsCancellation = True
        If bgw.IsBusy = False Then
            ProgressForm.Show()
            bgw.RunWorkerAsync(10)
        End If

        '********THIS LINE: bgwDone.WaitOne() MAKES A BIG DIFFERENCE*******
        bgwDone.WaitOne()
        MsgBox("1")
        MsgBox("2")
        Cursor = Cursors.Default
    End Sub

    'BackgroundWorker.RunWorkerAsync raises the DoWork event
    Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgw.DoWork
        Dim numToDo As Integer = CInt(e.Argument)
        For n As Integer = 1 To numToDo
            If bgw.CancellationPending Then
                Exit For
            End If
            System.Threading.Thread.Sleep(200)
            bgw.ReportProgress(n * 10)
        Next
        bgwDone.Set()
    End Sub

    'ReportProgress raises the ProgressChanged event
    Private Sub bgw_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
        ProgressForm.UpdateProgress(e.ProgressPercentage)
    End Sub

    Private Sub bgw_RunWorkerCompleted(sender As Object,
             e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
        ProgressForm.Close()
    End Sub

还有我的带有 ProgressBar 的表单:

Public Class ProgressForm
    Private Sub ButtonCancel_Click(sender As Object, e As EventArgs) Handles ButtonCancel.Click
        Form1.bgwCancelAsync()
    End Sub  

    Public Sub UpdateProgress(pct As Integer)
        ProgressBar1.Value = pct
        ProgressBar1.Refresh()
    End Sub
End Class

【问题讨论】:

  • 了解您要做什么可能会有所帮助。您正在 UI 线程上调用 WaitOne,这看起来很奇怪。如果您想在DoWork 中模拟长时间运行的任务,请将 Sleep 和 Report 放入循环中,否则它们只会发生一次。此外,您似乎正在使用默认表单实例(即类名,Form1ProgressForm),这通常是线程的问题。
  • @Plutonix 睡眠和报告在循环中。没有WaitOne,报告工作正常。我的实际应用是解析 CNC G 代码,进行更改。实际上没有用于生产用途的 GUI,仅用于初始设置和测试。文件名作为命令行参数提供给它。它按原样工作正常,但我认为添加进度条会很好,因为较大的文件需要几秒钟。 ProgressForm 这个名字怎么会惹麻烦?
  • 是的,它们在循环内。抱歉,我想我当时累了和/或饿了。

标签: vb.net progress-bar backgroundworker


【解决方案1】:

我不确定您要完成什么。但您的某些代码似乎试图破坏BackGroundWorker 的目的:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Cursor = Cursors.WaitCursor
    bgw = New BackgroundWorker
    ...
    If bgw.IsBusy = False Then
        ProgressForm.Show()
        bgw.RunWorkerAsync(10)
    End If

    bgwDone.WaitOne()
    MsgBox("1")
    MsgBox("2")
    Cursor = Cursors.Default
End Sub

BackgroundWorker 的目的是在另一个线程上执行一些长时间运行的任务并让 UI 响应。我不确定仅“需要几秒钟”的任务是否符合长时间运行的任务。

  • 鉴于此,为什么在 BGW 运行时使用 WaitCursor?让 UI 保持响应式的关键是允许用户在此期间做其他事情。
  • bgw.IsBusy 的测试永远不会是真的 - 你只是在 3 行之前创建了它。再次单击该按钮,您将创建另一个 BGW。
  • 单击中的其余代码看起来像您希望或期望代码在BGW 完成后继续在下一行。这不是它的工作原理。
    • 如果应用无法在这些任务未完成的情况下继续运行,请禁用任何可让用户前往其他地方的功能,直到工作人员完成或:
    • 放弃工作人员并将表单置于等待模式 (Me.UseWaitCursor) 直到加载内容。这并不排除ProgressBar
  • 在应用程序将在不同时间使用不同工作人员的情况下,专用的进度表可能很有意义。 StatusBar 可以包含 ProgressBar 并且更加微妙(也许合适,因为它一个状态元素)。

因此,修改并使用进度报告器的表单实例:

主窗体

Private WithEvents bgw As BackgroundWorker
Private frmProg As ProgressForm

Public Sub New()
    ' This call is required by the designer.
    InitializeComponent()
    ' Add any initialization after the InitializeComponent() call.
    bgw = New BackgroundWorker
End Sub

Private Sub btnLoadAll_Click(sender As Object, e As EventArgs) Handles btnLoadAll.Click

    bgw.WorkerReportsProgress = True
    bgw.WorkerSupportsCancellation = True

    If bgw.IsBusy = False Then
        ' create ProgressForm instance if needed
        If frmProg Is Nothing Then frmProg = New ProgressForm
        frmProg.Show()
        bgw.RunWorkerAsync(78)
    End If
    btnLoadAll.Enabled = False
End Sub

Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgw.DoWork
    ' multiple workers can use the same event
    Dim thisWorker = DirectCast(sender, BackgroundWorker)
    Dim count = Convert.ToInt32(e.Argument)

    For n As Integer = 1 To count
        If thisWorker.CancellationPending Then
            Exit For
        End If
        ' Fake work:
        System.Threading.Thread.Sleep(50)
        ' dont assume the size of the job if
        ' there are multiple BGW or tasks
        thisWorker.ReportProgress(Convert.ToInt32((n / count) * 100))
    Next
End Sub

Private Sub bgw_ProgressChanged(sender As Object,
                        e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
    frmProg.UpdateProgress(e.ProgressPercentage)
End Sub

Private Sub bgw_RunWorkerCompleted(sender As Object,
                        e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
    If e.Error IsNot Nothing Then
        '... ToDo
    ElseIf e.Cancelled Then
        '... ToDo
    Else
        frmProg.Close()
        ' avoid 'cannot access disposed object':
        frmProg = Nothing

        Me.btnNextStep.Enabled = True
        btnLoadAll.Enabled = True
    End If

End Sub

应用可以自动继续,而不是启用“下一步”按钮。这取决于应用程序。

【讨论】:

  • 谢谢。我确实有BackgroundWorker,试图将其作为事后的想法来实现。不会有“下一步”按钮,所以我希望应用程序自动继续,但只有在工作人员完成后。这就是我尝试 WaitOne() 的原因,它仍然具有以前的问题效果。问题中的 Msgbox() 调用代表随后在我的应用程序中的子项。在这种情况下,BackgroundWorker 可能不是刷新 ProgressBar 的最佳方式。
  • 您不必“等待 bgw 完成”(标题),它会在完成时告诉您。在这发生之前不要做任何事情。但正如我所说,只需要几秒钟的东西并不真正需要 ProgressBar,IMO
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多