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

时间:2013-06-13 19:34:04

标签: c# multithreading winforms backgroundworker ui-thread

我有一个简单的Windows窗体应用程序,它使用了几个BackgroundWorker元素,以及在后台执行操作。在开发过程中,我注意到我能够在.Text方法中执行get和设置Label的DoWork()值,但无法获取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 个答案:

答案 0 :(得分:0)

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

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

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

只是因为它在你第一次使用时没有崩溃并不意味着它会起作用。这意味着整个代码中可能出现竞争条件,这取决于其他线程同时访问共享资源的情况。它可以在你测试它时起作用,它可能会反复工作,但如果只是出现了正确的情况,就会发生各种各样的坏事。也许程序会崩溃,也许数据会被破坏,也许事情就不会起作用,或者可能会发生各种奇怪和琐碎的事情。如果这样的代码被设置到客户端,在那里你突然让很多不同的人以很多不同的和意想不到的方式执行代码,以及在非常不同类型的环境中,遇到问题的可能性没有遇到在你自己的测试中显着上升。

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

另外值得注意的是,在完全独立的说明中,组织代码以使您的UI和表示逻辑与业务逻辑分离是非常有帮助的;混合它们会导致难以推理,理解,维护或改变的应用程序。