Eccezione durante la chiusura di Form (thread + invoke)

Ho appena iniziato a conoscere thread e methodinvoking in c #, ma mi sono imbattuto in un problema di cui non sono riuscito a trovare la soluzione.

Ho creato un programma di base in formato C # che aggiorna e visualizza un numero, avviando una discussione e invocando il delegato.

Avvio del nuovo thread su Form1_load:

private void Form1_Load(object sender, EventArgs e) { t = new System.Threading.Thread(DoThisAllTheTime); t.Start(); } 

Public void DoThisAllTheTime (che continua ad aggiornare il numero):

 public void DoThisAllTheTime() { while(true) { if (!this.IsDisposed) { number += 1; MethodInvoker yolo = delegate() { label1.Text = number.ToString(); }; this.Invoke(yolo); } } } 

Ora quando faccio clic sul pulsante X del modulo, ottengo la seguente eccezione:

‘Un’eccezione non gestita di tipo’ System.ObjectDisposedException ‘si è verificata in System.Windows.Forms.dll

Imansible aggiornare un object eliminato ”

Mentre in realtà ho controllato se il modulo fosse stato smaltito o meno.

EDIT: ho aggiunto catch (ObjectDisposedException ex) al codice che ha risolto il problema. Codice di lavoro:

  public void DoThisAllTheTime() { while(true) { number += 1; try { MethodInvoker yolo = delegate() { label1.Text = number.ToString(); }; this.Invoke(yolo); } catch (ObjectDisposedException ex) { t.Abort(); } } } 

La tua chiamata a this.IsDisposed è sempre obsoleto. È necessario intercettare l’evento di chiusura del modulo e interrompere esplicitamente il thread. Quindi non dovrai assolutamente eseguire il test IsDisposed .

Ci sono molti modi in cui puoi farlo. Personalmente, vorrei usare lo spazio dei nomi System.Threading.Tasks , ma se vuoi mantenere l’utilizzo di System.Threading , devi definire una variabile membro _updateThread e avviarla nel tuo evento load:

 _updateThread = new System.Threading.Thread(DoThisAllTheTime); _updateThread.Start(); 

Quindi nel tuo evento di chiusura:

 private void Form1_Closing(object sender, CancelEventArgs e) { _stopCounting = true; _updateThread.Join(); } 

Infine, sostituisci il test IsDisposed con un controllo sul valore della nuova variabile membro _stopCounting :

 public void DoThisAllTheTime() { MethodInvoker yolo = delegate() { label1.Text = number.ToString(); }; while(!_stopCounting) { number += 1; this.Invoke(yolo); } } 

Basta mettere questo override nella tua class form:

 protected override void OnClosing(CancelEventArgs e) { t.Abort(); base.OnClosing(e); } 

Ho avuto un problema simile. Il mio Windows Form ha questo thread che preleva i dati dal database e aggiorna di conseguenza un listview. Il solo fatto di interrompere il thread non era abbastanza. A volte, quando il modulo era chiuso, poteva comparire l’errore perché alcuni eventi erano arrivati ​​dopo lo smaltimento del modulo. La soluzione era prima di interrompere con grazia il thread (come descritto nella risposta di Rob) e quindi concedere il tempo al messaggio pump per inviare tutti gli eventi al modulo, chiamando DoEvents.

La chiamata a DoEvents si verifica solo una volta e c’è un posto e un tempo giusti per farlo.

Nell’evento OnClosing , dopo essersi assicurati che tutti i thread siano terminati, chiamare Application.DoEvents () per elaborare tutti i messaggi Windows attualmente nella coda dei messaggi.

Proprio come questo:

 private void Form1_Closing(object sender, CancelEventArgs e) { // your thread graceful stop and cleaning code goes here. // borrowing Rob's example: _stopCounting = true; if (!_updateThread.Join(2000)) // give it 2 secs to stop gracefully _updateThread.Abort(); // if it won't play nice then abort // wait for the messaging pump do its thing Application.DoEvents(); } 

Se puoi, evita di chiamare Thread.Abort (). Soprattutto se il thread utilizza tutto ciò che è Usa e getta. Invece si potrebbe avere il thread “fermarsi con grazia”. La risposta di Rob è corretta, un altro buon esempio è descritto in https://stackoverflow.com/a/10669337

 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Thread.CurrentThread.Abort(); }