2010-10-24 7 views
5

Ich habe kürzlich eines dieser wirklich schlechten Interviews gehabt, wo sie einen guten Polizisten/einen schlechten Polizisten spielen. Was auch immer ich antwortete, war nicht gut genug für einen von ihnen, und mein Selbstvertrauen schwoll von Minute zu Minute. Seine letzte Frage, die mich wirklich verwirrte, war die folgende:Interview Frage: Wenn Control.InvokeRequired verwenden Sie Control.Invoke oder Control.BeginInvoke?

Wenn ein Steuerelement InvokeRequired benötigen würde, würde es einen Unterschied in tun. Invoke oder. BeginInvoke?

Lassen Sie mich Ihnen ein Beispiel, wie ich es verstehe:

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; 
    } 
} 

ich erwähnt, dass BeginInvoke es asynchron tun würde, während Invoke den UI-Thread werden Einhalt zu gebieten würde, bis ihr ausgeführt. Aber das war nicht gut genug. Trotzdem verstehe ich die Performance-Implikation hier nicht, wenn ich stattdessen Invoke verwende. Darf mich bitte jemand aufklären?

Antwort

15

Invoke hält die UI Thread nicht an. Es blockiert den Aufruf von Thread von fortgesetzt, bis der UI-Thread abgeschlossen ist.

Also wirklich, die Frage ist, ob die Hintergrundoperation fortgesetzt werden soll, bevor die UI die Aktualisierung beendet hat. Normalerweise glaube ich, ist der Fall - zum Beispiel, wenn Sie nur einen Fortschrittsbericht auf der Benutzeroberfläche bereitstellen, wollen Sie nicht aufhören zu arbeiten, nur weil der UI-Thread noch nicht aufgeholt hat.

Auf der anderen Seite, wenn Sie benötigen Holen Sie sich etwas aus dem UI-Thread (was ziemlich selten ist, zugegeben) dann möchten Sie stattdessen Invoke verwenden. Ich würde sagen, Sie sollten BeginInvoke verwenden, es sei denn, Sie haben einen bestimmten Grund, Invoke zu verwenden. Wie auch immer, Sie sollten den Unterschied verstehen :)

+2

Aus diesem Grund ist 'Invoke' anfällig für Deadlocks, abhängig von Ihrer Thread-Sicherheitseinstellung –

0

Natürlich, wenn Sie die asnyc-Version verwenden, kann Ihr Hintergrund-Thread sofort fortgesetzt werden, ohne auf den Kontextwechsel zu warten.

Normalerweise sollte das schneller sein (für den Hintergrundthread), von dem was ich unter verstehe.

5

Ein sehr offensichtlicher Anwendungsfall für Invoke() ist, wenn Sie eine Methode aufrufen müssen, deren Rückgabewert Sie benötigen. Nur Invoke() kann dies für Sie bereitstellen, es gibt Object, den Rückgabewert der Methode, zurück.

Eine schwächere ist, wo Ihr Worker-Thread Ergebnisse schneller produziert, als der UI-Thread mithalten kann. Wenn Sie Invoke() verwenden, wird der Worker gedrosselt, und die Aufrufliste kann nicht ohne Bindung erweitert werden. Dies ist jedoch nur ein Pflaster für ein größeres Problem, es macht keinen Sinn, die Benutzeroberfläche schneller zu aktualisieren als ein Mensch wahrnehmen kann. Einmal alle 40 Millisekunden sieht das menschliche Auge glatt aus. Sie möchten immer noch Invoke() verwenden, wenn der UI-Thread immer noch zu viel Zeit benötigt, um die Ergebnissammlung zu verarbeiten. Klassische Anzeichen dafür, dass ein solches Problem vorliegt, sind das Einfrieren des UI-Threads, das Vermeiden von Malen und Reagieren auf Maus- und Tastaturereignisse, da es durch Aufrufanforderungen vollständig überlastet wird. Und der UI-Thread blieb eine Weile nicht mehr an, nachdem der Worker den Vorgang beendet hatte.

Ein weiterer Fall sind Sperranforderungen für die Objekte, die Sie an BeginInvoke() übergeben. Wenn Sie Invoke() verwenden, ist keine Sperrung erforderlich. Der UI-Thread und der Worker-Thread können nicht gleichzeitig auf das Objekt zugreifen. Bei BeginInvoke ist dies nicht der Fall, es wird weitergefahren und wenn der Thread dasselbe Objekt verwendet, müssen Sie das Objekt sowohl im UI-Thread als auch im Worker mit einer Sperre schützen.Wenn diese Sperre verhindert, dass der Worker Fortschritte macht, können Sie auch Invoke() verwenden. Das ist ziemlich ungewöhnlich, es dauert sehr lange, bis der Hauptthread mit der Ausführung des Delegaten beginnt. Es ist immer eine gute Idee, nach dem Aufruf eine neue Instanz des Objekts zu erstellen, sodass keine Sperre erforderlich ist.