【问题标题】:Simple Multi Threading Progress Bar Example简单的多线程进度条示例
【发布时间】:2015-07-31 13:15:47
【问题描述】:

在我的项目中,我需要通过使用进度条来通知用户某些操作的进度。 现在,我知道有很多线程解释如何做到这一点,但我尝试的每个示例似乎都没有按预期工作......

我发现的最简单的示例是在 Microsoft 支持 (here) 上,但我在运行应用程序时似乎收到以下错误:

跨线程操作无效:控件“ProgressBar1”从创建它的线程以外的线程访问。

这是完全组织好的代码:

Imports System.Threading

Public Class Form1
    Private trd As Thread

    Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
        MessageBox.Show("This is the main thread")
    End Sub

    Private Sub ThreadTask()
        Dim stp As Integer
        Dim newval As Integer
        Dim rnd As New Random()

        Do
            stp = ProgressBar1.Step * rnd.Next(-1, 2)
            newval = ProgressBar1.Value + stp
            If newval > ProgressBar1.Maximum Then
                newval = ProgressBar1.Maximum
            ElseIf newval < ProgressBar1.Minimum Then
                newval = ProgressBar1.Minimum
            End If

            ProgressBar1.Value = newval

            Thread.Sleep(100)
        Loop
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        trd = New Thread(AddressOf ThreadTask)
        trd.IsBackground = True
        trd.Start()
    End Sub
End Class

现在,在对委托和调用方法进行了一些搜索和阅读之后,我在 ThreadTask 方法的开头添加了以下代码行:

If Me.InvokeRequired Then
    Me.Invoke(New Action(AddressOf ThreadTask))
    Return
End If

添加上面的代码行后,我就可以运行应用程序了。但是,我无法使用 btnTest 按钮,甚至没有绘制按钮...

(由于声誉低,我仍然无法发布图片。对不起...)

我不知道我做错了什么。有人可以帮帮我吗?

【问题讨论】:

  • 后台工作人员旨在使此类事情变得简单。它包括一个 ReportProgress 方法和 ProgressChanged 事件,用于更新诸如进度条之类的内容。您的委托逻辑是向后的,您将使用 InvokeRequired(或只是 Invoke)从线程与控件对话
  • 在加载函数上运行线程已经可能是一个问题。创建一个按钮来运行线程,然后创建一个运行相同子而不将其放入线程的按钮。这可以很好地显示差异。如果需要调用,您可以使用“if me.invoerequired = true”检查。有很多超线程的好例子,一个应该有帮助的(对我来说):vbforums.com/…

标签: .net vb.net multithreading progress-bar invoke


【解决方案1】:

您不应该调用整个方法,而应该只调用更新进度条的代码。

而不是ProgressBar1.Value = newval

你应该用ProgressBar1.BeginInvoke(Sub() ProgressBar1.Value = newvalue)替换它

但是,使用BackgroundWorker 进行这种简单的更新会更容易

【讨论】:

  • 大声笑!我从没想过您可以在 BeginInvoke 方法上传递一个属性...感谢您的帮助:D
【解决方案2】:

正如所说,后台工作人员可以为您完成所有这些......

Dim bw As BackgroundWorker = New BackgroundWorker
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted 

然后你需要启动它并执行处理程序..

bw.runworkerasync



Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
    Try

            Dim lp As Integer = "0"

            for i = 0 to 100
                lp = lp + 1
                bw.ReportProgress((lp / sz) * 100)
            Next

        End If
    Catch x As System.Exception
        ''do some error handling

    End Try
End Sub

ProgressBar1 应该添加到表单中

Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
    ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)

   'do some reporting 
    ProgressBar1.Value = 0
        End Sub

我以前用过这个,没有遇到过跨线程冲突。

【讨论】:

  • 使用WithEvents 而不是自己添加所有的处理程序
  • 另外你需要设置 WorkerReportsProgress = True 属性,因为默认情况下这是 False
  • @Chris S Hummm... 我知道您可以将 Background Worker 与多线程一起用于此类情况,但我不确定它是否适用于我的项目。我在这里给出的例子是起点,我希望从那里开始,以使我的项目继续进行。如果一切都失败了,我想我会尝试后台工作人员。
  • 好吧。似乎最好的方法实际上是通过使用后台工作人员。现在一切都很好:)
  • 我正在使用 .Net 3.5。 VB.Net BackgroundWorker 是否已弃用或过时?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 2014-01-17
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多