2

我有一个简单的 Windows 窗体应用程序,它使用几个BackgroundWorker元素在后台执行操作。在开发过程中,我注意到我能够在方法中执行诸如获取和设置.TextLabel 的值之类的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);
}
4

1 回答 1

0

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

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

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

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

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

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

于 2013-06-13T20:11:38.757 回答