c# - trackid - moduli google cancellare risposte




Come posso rendere sicuri i callback degli eventi nei miei moduli di vittoria? (4)

Quando ti iscrivi a un evento su un oggetto all'interno di un modulo, stai essenzialmente consegnando il controllo del tuo metodo di callback all'origine dell'evento. Non hai idea se la sorgente di eventi sceglierà di attivare l'evento su un thread diverso.

Il problema è che quando viene richiamato il callback, non si può presumere che sia possibile eseguire controlli di aggiornamento sul modulo perché a volte tali controlli generano un'espettazione se la richiamata dell'evento è stata chiamata su un thread diverso dal thread su cui è stato eseguito il modulo.


Come lazy programmer , ho un metodo molto pigro per farlo.

Quello che faccio è semplicemente questo.

private void DoInvoke(MethodInvoker del) {
    if (InvokeRequired) {
        Invoke(del);
    } else {
        del();
    }
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
    DoInvoke(delegate { lbl.Text = val; });
}

È possibile allineare DoInvoke all'interno della funzione o nasconderlo in una funzione separata per eseguire il lavoro sporco.

Tieni presente che puoi passare le funzioni direttamente nel metodo DoInvoke.

private void directPass() {
    DoInvoke(this.directInvoke);
}
private void directInvoke() {
    textLabel.Text = "Directly passed.";
}

Ecco i punti salienti:

  1. Non è possibile effettuare chiamate di controllo dell'interfaccia utente da un thread diverso da quello in cui sono stati creati (il thread del modulo).
  2. Le invocazioni dei delegati (ad esempio gli hook degli eventi) vengono attivate sullo stesso thread dell'oggetto che sta attivando l'evento.

Quindi, se hai un thread "motore" separato che fa un po 'di lavoro e hai un'interfaccia utente che osserva cambiamenti di stato che possono essere riflessi nell'interfaccia utente (come una barra di avanzamento o qualsiasi altra cosa), hai un problema. Il fuoco del motore è un evento oggetto modificato che è stato agganciato dal Modulo. Ma il callback delegato che il modulo registrato con il motore viene chiamato sul thread del motore ... non sul thread del modulo. E quindi non puoi aggiornare alcun controllo da quel callback. Doh!

BeginInvoke viene in soccorso. Usa questo semplice modello di codifica in tutti i tuoi metodi di callback e puoi essere sicuro che tutto andrà bene:

private delegate void EventArgsDelegate(object sender, EventArgs ea);

void SomethingHappened(object sender, EventArgs ea)
{
   //
   // Make sure this callback is on the correct thread
   //
   if (this.InvokeRequired)
   {
      this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
      return;
   }

   //
   // Do something with the event such as update a control
   //
   textBox1.Text = "Something happened";
}

È davvero semplice.

  1. Utilizzare InvokeRequired per scoprire se questo callback è avvenuto sulla thread corretta.
  2. In caso contrario, quindi reinvoke il callback sul thread corretto con gli stessi parametri. È possibile reinviare un metodo utilizzando i metodi Invoke (blocco) o BeginInvoke (non bloccanti).
  3. La volta successiva che viene chiamata la funzione, InvokeRequired restituisce false perché ora siamo sulla thread corretta e tutti sono felici.

Questo è un modo molto compatto per risolvere questo problema e rendere i tuoi moduli al sicuro da callback di eventi multi-thread.


Io uso molto metodi anonimi in questo scenario:

void SomethingHappened(object sender, EventArgs ea)
{
   MethodInvoker del = delegate{ textBox1.Text = "Something happened"; }; 
   InvokeRequired ? Invoke( del ) : del(); 
}

Per semplificare un po 'il codice di Simon, potresti usare il delegato di azione generico incorporato. Salva il peppering del codice con un gruppo di tipi delegati di cui non hai veramente bisogno. Inoltre, in .NET 3.5 hanno aggiunto un parametro params al metodo Invoke in modo da non dover definire una matrice temporanea.

void SomethingHappened(object sender, EventArgs ea)
{
   if (InvokeRequired)
   {
      Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
      return;
   }

   textBox1.Text = "Something happened";
}




events