C# 中的 BackgroundWorker2_RunWorkerCompleted 的无效跨线程操作
我收到一个没有意义的错误。
跨线程操作无效:控制“buttonOpenFile”是从创建它的线程以外的线程访问的。
在我的应用程序中,UI 线程触发 backgroundWorker1
,这当几乎完成时,会触发 backgroundWorker2
并等待其完成。 backgroundWorker1
等待 backgroundWorker2
完成,然后再完成。 AutoResetEvent 变量用于标记每个工作人员何时完成。在 backgroundWorker2_RunWorkerComplete
中,调用一个函数来重置表单控件。正是在这个 ResetFormControls()
函数中引发了异常。我认为在 RunWorkerCompleted 函数中修改表单控件是安全的。两个后台工作线程都是从 UI 线程实例化的。这是我正在做的事情的一个高度总结的版本:
AutoResetEvent evtProgrammingComplete_c = new AutoResetEvent(false);
AutoResetEvent evtResetComplete_c = new AutoResetEvent(false);
private void ResetFormControls()
{
toolStripProgressBar1.Enabled = false;
toolStripProgressBar1.RightToLeftLayout = false;
toolStripProgressBar1.Value = 0;
buttonInit.Enabled = true;
buttonOpenFile.Enabled = true; // Error occurs here.
buttonProgram.Enabled = true;
buttonAbort.Enabled = false;
buttonReset.Enabled = true;
checkBoxPeripheryModule.Enabled = true;
checkBoxVerbose.Enabled = true;
comboBoxComPort.Enabled = true;
groupBoxToolSettings.Enabled = true;
groupBoxNodeSettings.Enabled = true;
}
private void buttonProgram_Click(object sender, EventArgs e)
{
while (backgroundWorkerProgram.IsBusy)
backgroundWorkerProgram.CancelAsync();
backgroundWorkerProgram.RunWorkerAsync();
}
private void backgroundWorkerProgram_DoWork(object sender, DoWorkEventArgs e)
{
// Does a bunch of stuff...
if (tProgramStat_c == eProgramStat_t.DONE)
{
tProgramStat_c = eProgramStat_t.RESETTING;
while (backgroundWorkerReset.IsBusy)
backgroundWorkerReset.CancelAsync();
backgroundWorkerReset.RunWorkerAsync();
evtResetComplete_c.WaitOne(LONG_ACK_WAIT * 2);
if (tResetStat_c == eResetStat_t.COMPLETED)
tProgramStat_c = eProgramStat_t.DONE;
}
}
private void backgroundWorkerProgram_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Updates form to report complete. No problems here.
evtProgrammingComplete_c.Set();
backgroundWorkerProgram.Dispose();
}
private void backgroundWorkerReset_DoWork(object sender, DoWorkEventArgs e)
{
// Does a bunch of stuff...
if (tResetStat_c == eResetStat_t.COMPLETED)
if (tProgramStat_c == eProgramStat_t.RESETTING)
evtProgrammingComplete_c.WaitOne();
}
private void backgroundWorkerReset_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
CloseAllComms();
ResetFormControls();
evtResetComplete_c.Set();
backgroundWorkerReset.Dispose();
}
如果您有任何想法或建议,我们将不胜感激。我正在使用 Microsoft Visual C# 2008 Express Edition。谢谢。
I'm getting an error that does not make sense.
Cross-thread operation not valid: Control 'buttonOpenFile' accessed from a thread other than the thread it was created on.
In my application, the UI thread fires off backgroundWorker1
, which when almost complete fires off backgroundWorker2
and waits for it to complete. backgroundWorker1
waits for backgroundWorker2
to complete, before it completes. AutoResetEvent
variables are used to flag when each of the workers complete. In backgroundWorker2_RunWorkerComplete
a function is called that resets the form controls. It is in this ResetFormControls()
function where the exception is thrown. I thought it was safe to modify form controls in the RunWorkerCompleted
function. Both background workers are instantiated from the UI thread. Here is a greatly summarized version of what I am doing:
AutoResetEvent evtProgrammingComplete_c = new AutoResetEvent(false);
AutoResetEvent evtResetComplete_c = new AutoResetEvent(false);
private void ResetFormControls()
{
toolStripProgressBar1.Enabled = false;
toolStripProgressBar1.RightToLeftLayout = false;
toolStripProgressBar1.Value = 0;
buttonInit.Enabled = true;
buttonOpenFile.Enabled = true; // Error occurs here.
buttonProgram.Enabled = true;
buttonAbort.Enabled = false;
buttonReset.Enabled = true;
checkBoxPeripheryModule.Enabled = true;
checkBoxVerbose.Enabled = true;
comboBoxComPort.Enabled = true;
groupBoxToolSettings.Enabled = true;
groupBoxNodeSettings.Enabled = true;
}
private void buttonProgram_Click(object sender, EventArgs e)
{
while (backgroundWorkerProgram.IsBusy)
backgroundWorkerProgram.CancelAsync();
backgroundWorkerProgram.RunWorkerAsync();
}
private void backgroundWorkerProgram_DoWork(object sender, DoWorkEventArgs e)
{
// Does a bunch of stuff...
if (tProgramStat_c == eProgramStat_t.DONE)
{
tProgramStat_c = eProgramStat_t.RESETTING;
while (backgroundWorkerReset.IsBusy)
backgroundWorkerReset.CancelAsync();
backgroundWorkerReset.RunWorkerAsync();
evtResetComplete_c.WaitOne(LONG_ACK_WAIT * 2);
if (tResetStat_c == eResetStat_t.COMPLETED)
tProgramStat_c = eProgramStat_t.DONE;
}
}
private void backgroundWorkerProgram_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Updates form to report complete. No problems here.
evtProgrammingComplete_c.Set();
backgroundWorkerProgram.Dispose();
}
private void backgroundWorkerReset_DoWork(object sender, DoWorkEventArgs e)
{
// Does a bunch of stuff...
if (tResetStat_c == eResetStat_t.COMPLETED)
if (tProgramStat_c == eProgramStat_t.RESETTING)
evtProgrammingComplete_c.WaitOne();
}
private void backgroundWorkerReset_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
CloseAllComms();
ResetFormControls();
evtResetComplete_c.Set();
backgroundWorkerReset.Dispose();
}
Any thoughts or suggestions you may have would be appreciated. I am using Microsoft Visual C# 2008 Express Edition. Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
RunWorkerCompleted 将在启动BackgroundWorker 的线程上执行。由于您正在链接BackgroundWorkers(从1开始2),因此2的RunWorkerCompleted在1的线程上执行,而不是在UI线程上执行。
您需要使用 Invoke 编组回 UI 线程或将 UI 更新移至 1 的 RunWorkerCompleted。
我的建议是在更新 UI 时始终检查 InvokeRequired,这样您就无需担心它来自哪个线程。
RunWorkerCompleted is going to execute on the thread that started the BackgroundWorker. Since you're chaining BackgroundWorkers (starting 2 from 1), 2's RunWorkerCompleted is executing on 1's thread, not the UI thread.
You'll want to marshall back to the UI thread with Invoke or move the UI update to 1's RunWorkerCompleted.
My suggestion would be to always check for InvokeRequired when updating the UI, that way you don't need to worry about what thread it's coming from.
BackgroundWorker 对象不能嵌套。如果可能的话,我建议使用 .NET 4.0 任务,因为它们确实是嵌套的。
嵌套 BGW 只能通过使用 Nito.Async 库中的 ActionDispatcher 之类的东西来实现。
BackgroundWorker objects cannot be nested. I recommend using .NET 4.0 Tasks if at all possible, since they do nest.
Nesting BGWs is only possible by using something like ActionDispatcher from the Nito.Async library.
访问另一个线程中的控件永远不会是线程安全的。
您的问题是:“如何使用另一个线程访问由 UI 线程创建的控件?”
答案是调用您的控件。这是一个链接,您可以在其中查看代码示例允许它。
这能满足您的需要吗?
Accessing controls within another thread will never be threadsafe.
Your question is: "How can I access a control created by the UI thread using another thread?"
The answer is by invoking your control. Here's a link where you can see a code sample allowing it.
Does this do what you need?