Non valido cross-thread Operazioni dal BackgroundWorker2_RunWorkerCompleted in C #
-
26-09-2019 - |
Domanda
Ricevo un errore che non ha senso.
Cross-thread operation not valid: Control 'buttonOpenFile' accessed from a thread other than the thread it was created on.
Nella mia applicazione, i fuochi thread UI off backgroundWorker1
, che quando gli incendi quasi complete off backgroundWorker2
e aspetta che sia completato. backgroundWorker1
attende backgroundWorker2
al completo, prima del completamento. variabili AutoResetEvent
vengono utilizzate per la bandiera quando ciascuno dei lavoratori completare. In backgroundWorker2_RunWorkerComplete
viene chiamata una funzione che resetta i controlli di modulo. E 'in questa funzione ResetFormControls()
in cui viene lanciata l'eccezione. Ho pensato che fosse sicuro di modificare i controlli dei moduli nella funzione RunWorkerCompleted
. Entrambi i lavoratori di sfondo sono istanziati dal thread dell'interfaccia utente. Ecco una versione notevolmente riassunto di quello che sto facendo:
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();
}
Tutti i pensieri o suggerimenti che potete avere sarebbe apprezzato. Sto usando Microsoft Visual C # 2008 Express Edition. Grazie.
Soluzione
RunWorkerCompleted sta per eseguire sul thread che ha iniziato il BackgroundWorker. Dal momento che stai concatenamento BackgroundWorkers (a partire dal 2 1), 2 di RunWorkerCompleted è in esecuzione su thread 1 di, non il thread dell'interfaccia utente.
Ti consigliamo di Marshall torna al thread UI con Invoke o spostare l'aggiornamento dell'interfaccia utente a 1 di RunWorkerCompleted.
Il mio suggerimento sarebbe quello di controllare sempre per InvokeRequired quando si aggiorna l'interfaccia utente, in questo modo non c'è bisogno di preoccuparsi di ciò che filo è venuta da.
Altri suggerimenti
oggetti BackgroundWorker non possono essere nidificate. Mi consiglia di utilizzare .NET 4.0 Compiti se possibile, dal momento che fanno il nido.
Nesting BGWs è possibile solo utilizzando qualcosa come ActionDispatcher dal Nito.Async biblioteca.
Accesso controlli all'interno di un altro thread non sarà mai threadsafe.
La tua domanda è: "Come posso accedere ad un controllo creato dal thread UI utilizzando un altro thread"
La risposta è invocando il vostro controllo. Ecco un dove si può vedere un esempio di codice permettendo così.
Questo fa quello che ti serve?