9

我是线程世界的新手,但我正在开发的应用程序的某些方面要求我使用 BackgroundWorker 控件来防止 UI 在执行某些文件操作时冻结。

我要做的是从 BackgroundWorker 中更新几个表单标签。在我很快发现我无法访问不是在同一个线程中创建的控件之前从未使用过这个,所以经过一些研究,我实现了以下代码,似乎可以让一切正常工作:

Private Delegate Sub DelegateUpdateStatus(ByVal statusText As String, ByRef currentFile As String)

Private Sub UpdateStatus(ByVal statusText As String, ByVal currentFile As String)

    If InvokeRequired Then
        Invoke(Sub() LblStatus.Text = statusText)
        Invoke(Sub() LblCurrentFile.Text = currentFile)
    Else
        LblStatus.Text = statusText
        LblCurrentFile.Text = currentFile
    End If

End Sub

但问题是,我不明白这段代码在做什么,或者为什么需要它。

我已经做了一些研究,但我没有对这类事情做过任何实际的工作,而且我读过的大多数文章都假设有某种先验知识。

我希望了解的三个主要内容:

  • 为什么需要此代码(例如,为什么不能直接从 BackgroundWorker 访问控件)
  • 什么是委托,以及何时需要使用它
  • Invoke 方法的作用以及我使用 InvokeRequired 检查的内容

如前所述,线程仍然是一个相当陌生的概念,所以任何简单的英语答案都会非常有帮助 - 谢谢!

编辑:感谢大家到目前为止的回复。我做了一些进一步的阅读,我想知道我是否以正确的方式去做。我使用 BackgroundWorker 的原因是为了确保 UI 在我执行文件操作时保持响应。问题是,我仍然需要等到 BackgroundWorker 完成它的工作,这样我才能返回一个指示操作成功的布尔值。有一些方法可以解决这个问题,但从我的阅读来看,不得不等待 BackgroundWorker 完成其工作,这违背了使用它的初衷。那么,防止 UI 锁定的最佳方法是什么?

4

3 回答 3

12

好的,到目前为止做得很好。

让我们从第 1 点开始。

如何:对 Windows 窗体控件进行线程安全调用

如果使用多线程来提高 Windows 窗体应用程序的性能,则必须确保以线程安全的方式调用控件。

对 Windows 窗体控件的访问本质上不是线程安全的。如果您有两个或更多线程操作控件的状态,则可能会强制控件进入不一致的状态。

因此,正如您所看到的,您需要确保在更改控件的状态时,它是以线程安全的方式完成的。

现在,属性Control.InvokeRequired检查您正在执行的代码是否在与最初创建控件的线程不同的线程上。

如果是,我们需要某种方式来调用该原始线程上的代码。

为此,您需要使用Control.Invoke 方法在原始线程上执行此类代码。

在拥有控件的基础窗口句柄的线程上执行委托。

现在的问题是,您需要告诉该线程它应该执行什么,这是使用委托完成的。

表示一个委托,它是一种数据结构,它引用静态方法或类实例和该类的实例方法。

现在,我要推荐的最后一件事是你看看委托、匿名委托、匿名方法和 Lamda 表达式之间的区别。

于 2013-06-27T04:23:22.537 回答
4

InvokeRequired问“我在正确的线程上吗?”,如果是这样继续,否则我需要一个委托 - 在你的代码中你会看到一个 lambda Sub

 Invoke(Sub() LblStatus.Text = statusText)

另一种可行的方法是将结果定向到 diff Sub 例程(委托已跨到正确的线程),但在这里我们能够在 Invoke 方法本身内运行 Sub - 只是一种更简单的方法它。

每当您使用单独的线程进行异步工作时,都需要委托。

调用 - MSDN

于 2013-06-27T04:21:50.487 回答
4
  1. BackgroundWorker 是另一种拥有线程并从中进行线程安全调用的方法。on ProgressChangedRunWorkerCompleted您可以对 UI 进行线程安全调用

  2. 委托只是对方法的引用

Control.Invoke(委托)

在拥有控件的基础窗口句柄的线程上执行指定的委托。

Control.InvokeRequired

获取一个值,该值指示调用者在对控件进行方法调用时是否必须调用调用方法,因为调用者位于与创建控件的线程不同的线程上。

以及为什么我们需要所有这些东西。从 msdn 文档中得到了澄清:

如果您有两个或更多线程操作控件的状态,则可能会强制控件进入不一致的状态。其他与线程相关的错误是可能的,例如竞争条件和死锁。确保以线程安全的方式执行对控件的访问非常重要。

所以为了安全起见,我们必须实现它,否则我们的工作线程和 UI 线程可能都尝试同时访问数据成员,这将对我们的应用程序造成严重破坏。因此,与其禁用它并让您的应用程序有可能崩溃,不如使用线程安全模式。

于 2013-06-27T04:28:55.233 回答