2010-10-24 5 views
5

Ho avuto di recente una di quelle interviste davvero brutte, in cui giocano con loro un poliziotto buono/cattivo. Qualunque cosa abbia risposto non era abbastanza buono per uno di loro e la mia fiducia stava diminuendo di minuto in minuto. La sua ultima domanda che mi ha davvero confuso era la seguente:Domanda intervista: quando Control.InvokeRequired usi Control.Invoke o Control.BeginInvoke?

se un controllo avrebbe bisogno di InvokeRequired ci sarebbe una differenza nel fare .Invoke o .BeginInvoke?

Lasciate che vi mostri un esempio, come ho capito:

public delegate string WorkLongDelegate(int i); 

var del = new WorkLongDelegate(WorkLong); 
var callback = new AsyncCallback(CallBack); 
del.BeginInvoke(3000, callback, del); 

public string WorkLong(int i) 
{ 
     Thread.Sleep(i); 
     return (string.Format("Work was done within {0} seconds.", i));    
} 

private void CallBack(IAsyncResult ar) 
{ 
    var del = (WorkLongDelegate) ar.AsyncState; 
    SetText2(del.EndInvoke(ar)); 
} 

private void SetText2(string s) 
{ 
    if(InvokeRequired) 
    { 
     // What is the difference between BeginInvoke and Invoke in below? 
     BeginInvoke(new MethodInvoker(() => textBox1.Text = s)); 
    } 
    else 
    { 
     textBox1.Text = s; 
    } 
} 

ho detto che BeginInvoke avrebbe fatto in modo asincrono mentre Invoke sarebbe arrestare il thread dell'interfaccia utente fino alla sua esecuzione. Ma non era abbastanza buono. Ciò nonostante, non capisco l'implicazione delle prestazioni qui se usassi invece Invoke. Qualcuno può illuminarmi?

risposta

15

Invoke non interrompe il thread UI. Blocca il chiamando il thread da continuare fino al completamento del thread dell'interfaccia utente.

Quindi, in realtà, la domanda è se si desidera che l'operazione in background continui prima che l'interfaccia utente abbia completato l'aggiornamento. Di solito, credo che questo sia il caso: ad esempio, se stai solo fornendo un rapporto sull'interfaccia utente, non vuoi smettere di funzionare solo perché il thread dell'interfaccia utente non è ancora stato raggiunto.

D'altra parte, se avete bisogno di prendere qualcosa dal thread dell'interfaccia utente (che è piuttosto raro, è vero), allora si potrebbe desiderare di utilizzare Invoke invece. Direi che dovresti usare BeginInvoke a meno che tu non abbia un motivo specifico per usare Invoke. Ma in entrambi i casi, dovresti capire la differenza :)

+2

Per questo motivo, 'Invoke' è vulnerabile a deadlock a seconda della configurazione di sicurezza del thread –

0

Naturalmente, se si utilizza la versione asnyc, il thread in background può continuare immediatamente, senza attendere l'interruttore di contesto.

Di solito, questo dovrebbe essere più veloce (per il thread in background), da quello che faccio unterstand.

5

Un caso di utilizzo molto ovvio per Invoke() è quando è necessario richiamare un metodo il cui valore di ritorno è necessario. Solo Invoke() può fornire ciò per te, restituisce Object, il metodo restituisce il valore.

Uno più debole è dove il thread di lavoro sta producendo risultati a una velocità molto più veloce di quanto il thread dell'interfaccia utente possa tenere il passo. L'utilizzo di Invoke() limiterà il lavoratore e l'elenco dei richiami non potrà crescere senza vincoli. Questo però è solo un cerotto per un problema più grande, non ha senso aggiornare l'interfaccia utente più velocemente di quanto un umano possa percepire. Una volta ogni 40 millisecondi appare liscio all'occhio umano. Desideri comunque utilizzare Invoke() se il thread dell'interfaccia utente richiede ancora troppo tempo per elaborare la raccolta dei risultati. I classici segni di un problema di questo tipo sono il congelamento del thread dell'interfaccia utente, non andare in giro per dipingere e rispondere agli eventi mouse e tastiera perché è completamente sopraffatto invocando le richieste. E il thread dell'interfaccia utente non ha risposto per un po 'dopo che il lavoratore ha completato la corsa, impegnato a smaltire il backlog.

Un altro caso è il blocco dei requisiti per gli oggetti passati a BeginInvoke(). Nessun blocco è richiesto quando si utilizza Invoke(), il thread dell'interfaccia utente e il thread di lavoro non possono accedere all'oggetto nello stesso momento. Non è il caso di BeginInvoke, continua a funzionare e se il thread continua a utilizzare lo stesso oggetto, è necessario proteggere l'oggetto con un blocco sia nel thread dell'interfaccia utente sia nell'operatore.Se quel blocco impedisce al lavoratore di fare progressi, si potrebbe usare Invoke(). Questo è abbastanza raro, ci vuole molto tempo per il thread principale per avviare l'esecuzione del delegato. Ed è sempre una buona idea creare una nuova istanza dell'oggetto dopo averlo invocato, in modo che non sia necessario bloccarlo.