2015-01-29 11 views
8

Ich habe this question verfolgt und ich verstehe die Gründe für die beliebte (wenn auch noch nicht angenommene) Antwort von Peter Duniho. Insbesondere Ich weiß, daß nicht eine nachfolgende langlaufende Operation wartet den UI-Thread Block:Sollten erwartete, erwartete Operationen erwartet werden?

Das zweite Beispiel, das nicht während der asynchronen Operation nicht ergeben. Stattdessen erzwingen Sie durch Abrufen des Werts der content.Result-Eigenschaft, dass der aktuelle Thread wartet, bis der asynchrone Vorgang abgeschlossen ist.

Ich habe bestätigt, auch dies, denn meine eigenen Vorteil, etwa so:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var value1 = await Task.Run(async() => 
     { 
      await Task.Delay(5000); 
      return "Hello"; 
     }); 

    //NOTE: this one is not awaited... 
    var value2 = Task.Run(async() => 
     { 
      await Task.Delay(5000); 
      return value1.Substring(0, 3); 
     }); 

    System.Diagnostics.Debug.Print(value2.Result); //thus, UI freezes here after 5000 ms. 
} 

Aber jetzt frage ich mich: Sie brauchen zu await alle „awaitable“ Operationen innerhalb einer äußersten awaitable verschachtelt Betrieb? Zum Beispiel kann ich dies tun:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var value0 = await Task.Run(() => 
     { 
      var value1 = new Func<Task<string>>(async() => 
      { 
       await Task.Delay(5000); 
       return "hello"; 
      }).Invoke(); 

      var value2 = new Func<string, Task<string>>(async (string x) => 
       { 
        await Task.Delay(5000); 
        return x.Substring(0, 3); 
       }).Invoke(value1.Result); 

      return value2; 
     }); 

    System.Diagnostics.Debug.Print(value0); 
} 

Oder ich kann dies tun:

private async void button1_Click(object sender, EventArgs e) 
{ 
    //This time the lambda is async... 
    var value0 = await Task.Run(async() => 
     { 
      //we're awaiting here now... 
      var value1 = await new Func<Task<string>>(async() => 
      { 
       await Task.Delay(5000); 
       return "hello"; 
      }).Invoke(); 

      //and we're awaiting here now, too... 
      var value2 = await new Func<string, Task<string>>(async (string x) => 
       { 
        await Task.Delay(5000); 
        return x.Substring(0, 3); 
       }).Invoke(value1); 

      return value2; 
     }); 

    System.Diagnostics.Debug.Print(value0); 
} 

Und keiner von ihnen die Benutzeroberfläche einzufrieren. Welche ist vorzuziehen?

+0

ich sehr überrascht sein, wenn alle die gleiche Sache zu drucken, ich glaube, dass man eine Aufgabe Objekt druckt eher als sein Ergebnis. –

+0

@BenVoigt - Nein, bc In beiden Fällen wird die Aufgabe ausgepackt, da sie erwartet wird. –

+0

Aber eine davon ist das Umpacken einer zweiten Aufgabe. –

Antwort

13

Der letzte ist bevorzugt (wenn auch ziemlich chaotisch)

In TAP (Aufgabe basierte asynchrone Muster) Ein Task (und andere awaitables) einen asynchronen Vorgang darstellen. Sie haben grundsätzlich drei Möglichkeiten, diese Aufgaben Handhabung:

  • synchron Wait (DoAsync().Result, DoAsync().Wait()) - Blockieren der anrufende Thread, bis die Aufgabe abgeschlossen ist. Macht Ihre Anwendung verschwenderischer, weniger skalierbar, weniger reaktiv und anfällig für Deadlocks.
  • Warte asynchron (await DoAsync()) - Blockiert den aufrufenden Thread nicht. Es registriert grundsätzlich die Arbeit nach der await als eine Fortsetzung, die ausgeführt wird, nachdem die erwartete Aufgabe abgeschlossen ist.
  • Warten Sie nicht (DoAsync()) - Blockiert den aufrufenden Thread nicht, aber wartet auch nicht auf den Vorgang abzuschließen. Sie sind sich nicht bewusst von irgendwelchen während DoAsync geworfen Ausnahmen verarbeitet wird

Insbesondere ich weiß, dass nicht eine nachfolgende lang andauernde Operation erwartet wird

Also, nicht ganz der UI-Thread blockieren. Wenn Sie überhaupt nicht warten, wird nichts blockiert, aber Sie können nicht wissen, wann oder ob der Vorgang erfolgreich abgeschlossen wurde. Wenn Sie jedoch synchron warten, blockieren Sie den aufrufenden Thread und Sie haben möglicherweise Deadlocks, wenn Sie den UI-Thread blockieren.

Fazit: Sie sollten await Ihre Erwartungen so lange wie möglich (es ist nicht in Main zum Beispiel). Dazu gehören "verschachtelte async-await Operationen".

Über Ihr spezifisches Beispiel: Task.Run wird verwendet, um CPU-gebundene Arbeit zu einem ThreadPool-Thread zu entladen, was nicht das zu sein scheint, was Sie nachzuahmen versuchen.Wenn wir verwenden Task.Delay eine wirklich asynchronen Betrieb darzustellen (in der Regel I/O-gebunden) können wir „verschachtelt async-await“ haben ohne Task.Run:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var response = await SendAsync(); 
    Debug.WriteLine(response); 
} 

async Task<Response> SendAsync() 
{ 
    await SendRequestAsync(new Request()); 
    var response = await RecieveResponseAsync(); 
    return response; 
} 

async Task SendRequestAsync(Request request) 
{ 
    await Task.Delay(1000); // actual I/O operation 
} 

async Task<Response> RecieveResponseAsync() 
{ 
    await Task.Delay(1000); // actual I/O operation 
    return null; 
} 

Sie anonyme Teilnehmer statt Methoden verwenden können, aber es ist unangenehm, wenn Sie brauchen um die Typen zu definieren und sie selbst aufzurufen.

Wenn Sie diese Operation zu einem ThreadPool Faden zu Offload brauchen, fügen Sie einfach Task.Run:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var response = await Task.Run(() => SendAsync()); 
    Debug.WriteLine(response); 
} 
+0

Eigentlich lese ich deine Antwort noch einmal durch, und ich frage mich, ob du das bitte klarstellen kannst: sagst du, dass ich * nicht * verschachtelte Wartezeiten benutzen soll (was mein dritter Block Code tut - der eine nach "Oder kann ich das")? –

+0

@roryap Ich sage, dass, wenn die Operationen "async" sind, dann warten sie auf sie statt zu blockieren, egal wo sie sind. Ob "Task.Run" Null ist, einmal oder zweimal, hängt davon ab, was Sie eigentlich tun * (ich nehme an, "Task.Delay" ist hier nur ein Ersatz). – i3arnon

+1

@roryap Wenn Sie eine lange CPU-intensive Operation haben, die Sie auslagern möchten, verwenden Sie 'Task.Run', andernfalls rufen Sie einfach Ihre' asynchronen' Operationen auf und 'warten' Sie darauf. – i3arnon

Verwandte Themen