2013-07-17 21 views
6

Ich folgte ein Beispiel aus C# in Kürze. Laut dem Text soll der folgende Code nicht blockierend sein, aber ich finde, dass das Formular nicht angezeigt wird, bis die 5 Sekunden vergangen sind.Thread blockiert die UI

private void Form1_Load(object sender, EventArgs e) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start(); 

    Task<int> task = tcs.Task; 
    MessageBox.Show(task.Result.ToString()); 
} 

Ich habe das Gefühl, es ist etwas mit Thread.Sleep() zu tun und statt den neuen Thread zu schlafen setzen, es ist der Hauptthread einzuschläfern.

Warum blockiert es den UI-Thread?

+3

Aber Sie verwenden Ergebnis der Aufgabe direkt nach dem Einführen, so wird es warten, bis Ergebnis bereit .. – adt

+1

Sie sollten 'Task.Delay' verwenden, um eine Aufgabe zu erhalten, die in X Millisekunden statt dieser Methode abgeschlossen wird ist mehr Arbeit und schafft auch eine ganz neue Aufgabe, nur um dort sitzen zu bleiben und nichts zu tun (was ziemlich teuer ist). – Servy

+0

@Servy, gibt 'Task.Delay()' auch keine brandneue 'Task' zurück, die nur dort sitzt und nichts tut (wenn auch nicht auf einem neuen Thread)? –

Antwort

5

Wenn Sie Task.Result.ToString() (im MessageBox.Show) der Task Klasse aufrufen hat einen Mechanismus, der für wartet die Aufgabe, bevor sie tatsächlich beendet werden Sie das Ergebnis zu geben (wie es eigentlich nicht, bis die Task beendet hat. Hier ist mein Beweis :

private void Form1_Load(object sender, EventArgs e) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start(); 

    Task<int> task = tcs.Task; 
    Thread.Sleep(2500); 
    MessageBox.Show("Waited for 2.5secs on UI thread."); 
    MessageBox.Show(task.Result.ToString()); 
} 

Sie werden sehen, dass es Ihnen das 2.5sec Meldungsfeld vor dem messagebox mit 42 (in der Tat 2,5 Sekunden davor) zeigt hierfür ist

Was Sie suchen:.

private void Form1_Load(object sender, EventArgs e) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start(); 

    Task<int> task = tcs.Task; 
    task.ContinueWith(t => MessageBox.Show(t.Result.ToString())); 
} 

Dies wird nicht den UI-Thread einfrieren.

11

Wenn Sie versuchen, das Ergebnis der Aufgabe task.Result zu erhalten, wird der Haupt-Thread blockiert, bis die Aufgabe ihre Ausführung beendet (d. H. Das Ergebnis wird verfügbar sein). Verwenden Sie task.ContinueWith, wenn Sie nicht für Asynchron-Betrieb Fertigstellung warten möchten:

Task<int> task = tcs.Task; 
task.ContinueWith(t => {   
    MessageBox.Show(t.Result.ToString()); 
}); 

BTW gibt es nettes Feature in .NET 4.5 für abgehängte den Betrieb wieder aufnehmen, wenn Aufgabe abgeschlossen ist - Asynchron-Methoden:

private async void Form1_Load(object sender, EventArgs e) 
{ 
    var tcs = new TaskCompletionSource<int>(); 
    new Thread(() => { Thread.Sleep(2000); tcs.SetResult(42); }).Start(); 
    int result = await tcs.Task; 
    MessageBox.Show(result.ToString()); 
} 

Diese Methode gibt dem Aufrufer unmittelbar nach dem Start des Task-Ergebnisses die Kontrolle. Wenn das Ergebnis verfügbar ist, wird die Ausführung der Methode fortgesetzt und die Nachricht angezeigt.

Eigentlich @Servy zeigte in Kommentaren, Async-Methode, die void zurückgeben ist nicht sehr gute Praxis (z. B. für die Fehlerbehandlung), aber manchmal ist es in Ordnung, sie für Event-Handler zu verwenden.

+1

Ihre 'DoSomethingAsync' sollte wirklich eine' Task' zurückgeben, 'async void' sollte nur verwendet werden, wenn es absolut notwendig ist, und die Verwendung von' Task.Delay' macht die Implementierung dramatisch einfacher, einfacher und leistungsfähiger. – Servy

+0

@Servy Ich denke OP experimentiert mit TaskCompletionSource. Vereinbaren Sie die Rückgabe der Aufgabe –

Verwandte Themen