我收到了一個無意義的錯誤。來自BackgroundWorker2_RunWorkerCompleted in C#的無效跨線程操作
Cross-thread operation not valid: Control 'buttonOpenFile' accessed from a thread other than the thread it was created on.
在我的應用程序,用戶界面線程打完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速成版。謝謝。
從技術上講,2的RunWorkerCompleted將在完全不同的ThreadPool線程上運行,而不是在1的線程上運行。 – 2010-05-12 16:07:29
謝謝,Ragoczy。因此,如果我理解正確,'RunWorkerCompleted'運行在調用'RunWorkerAsync'的線程上,而不一定是實例化(創建/聲明)BackgroundWorker的線程。那是對的嗎? – 2010-05-12 16:10:10
RunWorkerCompleted似乎在隨機線程池線程上運行(我的快速測試恰好結束了重用相同的線程)除非RunWorkerAsync從主線程和實現ISynchronizeInvoke的類中調用 - 有趣的是,兩者似乎都是必需的有限的測試案例,可能還有更多。無論如何,如果您的ResetFormControls方法被更改爲檢查InvokeRequired和Invoke(如果有必要),您應該可以 - 因爲那樣可以讓您在將來實現其他線程選項(.net 4),同時保持您的Form接口線程安全。 – Ragoczy 2010-05-12 16:40:31