c# - 为什么我只能从 BackgroundWorker 访问某些 UI 控件属性?

标签 c# multithreading winforms backgroundworker ui-thread

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

我的印象是 BackgroundWorker 不应该有任何 UI 交互,这条规则有一些异常(exception)吗?

我不是在寻找从 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);
}

最佳答案

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

这是大多数 UI 元素的一个特性,它们将检测是否未从 UI 线程访问它们并主动抛出异常,以便开发人员在代码第一次运行时立即意识到他们正在不正确地访问控件。

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

仅仅因为它在您第一次使用时不会崩溃并不意味着它会起作用。这意味着整个代码中可能存在竞争条件,这些竞争条件可能会突然出现,具体取决于同时访问共享资源的其他线程。当你测试它时它可能会起作用,它可能会反复起作用,但如果恰好发生了一组正确的情况,那么各种坏事都可能发生。也许程序会崩溃,也许数据会被破坏,也许事情就是无法正常工作,或者可能会发生各种奇怪的事情。如果这样的代码被发送给客户,突然间让很多不同的人以许多不同的和意想不到的方式执行代码,以及在非常不同类型的环境中,那么很可能会遇到未遇到的问题在您自己的测试中急剧上升。

因此请花点时间确保您只从 UI 线程访问 UI 控件。

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

关于c# - 为什么我只能从 BackgroundWorker 访问某些 UI 控件属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17095535/

相关文章:

c# - 为什么我得到一个 OpenXmlUnknownElement?

c# - 比较两个列表的顺序变化

multithreading - 在非 VCL 应用程序中使用同步有危险吗?

.net - 对于重复 WinForms 数据,是否有比 DataGridView 更好的选择?

c# - Google c# Api,从 v2.3 更改为 v3

c# - Watin 不会手动触发 .change() 事件

c++ - 多线程通过命令行参数设置线程数或

c# - 防止 WinForms PictureBox 动画 GIF 在处理过程中暂停?

c# - 在 WinForms UserControl 构造函数中将 DesignMode 换成 LicenseManager.UsageMode 是否有任何注意事项?

c# - 如何自动化使用 Web 服务的任务