为啥我只能从 BackgroundWorker 访问一些 UI 控件属性?

Posted

技术标签:

【中文标题】为啥我只能从 BackgroundWorker 访问一些 UI 控件属性?【英文标题】:Why can I access only some UI control properties from BackgroundWorker?为什么我只能从 BackgroundWorker 访问一些 UI 控件属性? 【发布时间】:2013-06-10 08:07:16 【问题描述】:

我有一个简单的 Windows 窗体应用程序,它使用几个 BackgroundWorker 元素在后台执行操作。在开发过程中,我注意到我能够在 DoWork() 方法中执行诸如获取和设置 Label 的 .Text 值之类的操作,但无法获取 DrowDownList 的值。

我的印象是 BackgroundWorker 不应该有任何 UI 交互,这条规则有一些例外吗?

我不是在寻找从 BackgroundWorker 更新 UI 的建议,我更好奇是否有我可以采用的快捷方式,例如直接从 BackgroundWorker 更新 ToolStripStatusLabel 的文本。

这是一个简单的例子,worker 1 直接更新 UI,而 worker 2 使用进度更新:

private void button1_Click(object sender, EventArgs e)

    backgroundWorker1.RunWorkerAsync();


private void button2_Click(object sender, EventArgs e)

    backgroundWorker2.RunWorkerAsync();


private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

    toolStripStatusLabel1.Text = "Performing task 1...";
    performTaskOne();
    toolStripStatusLabel1.Text = "Task 1 done.";

    toolStripStatusLabel1.Text = "Performing task 2...";
    performTaskTwo();
    toolStripStatusLabel1.Text = "Task 2 done.";

    toolStripStatusLabel1.Text = "Performing task 3...";
    performTaskThree();
    toolStripStatusLabel1.Text = "Task 3 done.";


private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)

    backgroundWorker2.ReportProgress(0, "Performing task 1...");
    performTaskOne();
    backgroundWorker2.ReportProgress(20, "Task 1 done.");

    backgroundWorker2.ReportProgress(40, "Performing task 2...");
    performTaskTwo();
    backgroundWorker2.ReportProgress(60, "Task 2 done.");

    backgroundWorker2.ReportProgress(80, "Performing task 3...");
    performTaskThree();
    backgroundWorker2.ReportProgress(100, "Task 3 done.");


private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)

    string status = (string)e.UserState;
    toolStripStatusLabel1.Text = status;


private void performTaskThree()

    Thread.Sleep(1000);


private void performTaskTwo()

    Thread.Sleep(1000);


private void performTaskOne()

    Thread.Sleep(1000);

【问题讨论】:

我也遇到了同样的情况,不得不使用代表......但我不知道为什么 +1 任何示例代码?我认为这里没有限制,BackgroundWorker 只是某种lightweight 线程... BackgroundWorker 旨在对 UI 友好,但它仍然是后台线程,这意味着您需要执行线程安全的操作,例如使用委托对 UI 控件进行操作。捷径经常会回来咬你......很难。 添加了一个代码示例来说明这两种方法 @invertigo 我测试了代码,toolStripStatusLabel1.Text 更新正常。你的实际问题是什么?一点也不例外? 【参考方案1】:

您不应从后台线程访问或更新 UI。期间。

这是大多数 UI 元素的一个功能,如果它们没有被 UI 线程访问,它们会检测到并主动抛出异常,以便开发人员在代码第一次运行时立即意识到他们不正确地访问控件。

并非所有 UI 控件,也不是 UI 控件的所有方面,都具有此功能。某些成员被专门记录为可以在非 UI 线程中安全使用的对象的成员(例如,控件的 Invoke 方法显然可以在非 UI 线程中使用;这就是它存在的原因)。对于那些没有记录的,你不应该在非 UI 线程中使用它们。

仅仅因为它在您第一次使用时不会崩溃并不意味着它会起作用。这意味着整个代码中可能存在竞争条件,这取决于其他线程同时访问共享资源的情况。当你测试它时它可能会起作用,它可能会反复起作用,但如果发生正确的情况,各种坏事都可能发生。也许程序会崩溃,也许数据会损坏,也许事情不会正常工作,或者可能会发生各种奇怪和古怪的事情。如果将此类代码发送给客户端,您会突然让很多不同的人以很多不同和意想不到的方式执行代码,以及在非常不同类型的环境中,遇到未遇到问题的机会在您自己的测试中大幅上升。

所以花点时间确保您只能从 UI 线程访问 UI 控件。

另外值得注意的是,在一个完全独立的注释中,它极大地有助于组织您的代码以使您的 UI 和表示逻辑与您的业务逻辑分离;将它们混合会导致应用程序更难推理、理解、维护或更改。

【讨论】:

以上是关于为啥我只能从 BackgroundWorker 访问一些 UI 控件属性?的主要内容,如果未能解决你的问题,请参考以下文章

为啥相同的代码在我的 BackGroundWorker 线程中比在我的 GUI 线程中慢得多?

从 BackgroundWorker 运行时 CrystalReportViewer.RefreshReport 挂起

从 backgroundworker 迁移到 async / await 方法

BackgroundWorker控件使用

BackgroundWorker RunWorkerCompleted 事件

如何将 BackgroundWorker 的结果设置为 TextView