Async / Warten in C # verstehen

75

Ich fange an, in C # 5.0 etwas über Async / Warten zu lernen, und ich verstehe es überhaupt nicht. Ich verstehe nicht, wie es für Parallelität verwendet werden kann. Ich habe das folgende sehr grundlegende Programm ausprobiert:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task task1 = Task1();
            Task task2 = Task2();

            Task.WaitAll(task1, task2);

            Debug.WriteLine("Finished main method");
        }

        public static async Task Task1()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
            Debug.WriteLine("Finished Task1");
        }

        public static async Task Task2()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
            Debug.WriteLine("Finished Task2");
        }

    }
}

Dieses Programm blockiert nur den Aufruf von Task.WaitAll()und wird nie beendet, aber ich verstehe nicht warum. Ich bin sicher, ich vermisse nur etwas Einfaches oder habe einfach nicht das richtige mentale Modell dafür, und keiner der Blogs oder MSDN-Artikel, die es gibt, hilft.

Alex Marshall
quelle
5
await new Task....Verwenden Sie stattdessenawait Task.Delay(...);
David Heffernan
docs.microsoft.com/en-us/dotnet/csharp/programming-guide/… ist ein großartiges Intro
David Thielen

Antworten:

68

Ich empfehle Ihnen, mit meiner Einführung in async/await und der offiziellen MSDN-Dokumentation zu TAP zu beginnen .

Wie ich in meinem Intro-Blog-Beitrag erwähne, gibt es mehrere TaskMitglieder, die Überbleibsel der TPL sind und keine Verwendung in reinem asyncCode haben. new Taskund Task.Startsollte durch Task.Run(oder TaskFactory.StartNew) ersetzt werden. Ebenso Thread.Sleepsollte durch ersetzt werden Task.Delay.

Schließlich empfehle ich, dass Sie nicht verwenden Task.WaitAll; Ihre Konsolen-App sollte nur Waitauf einer einzigen App Taskverwendet werden Task.WhenAll. Mit all diesen Änderungen würde Ihr Code folgendermaßen aussehen:

class Program
{
    static void Main(string[] args)
    {
        MainAsync().Wait();
    }

    public static async Task MainAsync()
    {
        Task task1 = Task1();
        Task task2 = Task2();

        await Task.WhenAll(task1, task2);

        Debug.WriteLine("Finished main method");
    }

    public static async Task Task1()
    {
        await Task.Delay(5000);
        Debug.WriteLine("Finished Task1");
    }

    public static async Task Task2()
    {
        await Task.Delay(10000);
        Debug.WriteLine("Finished Task2");
    }
}
Stephen Cleary
quelle
Vielen Dank für Ihre Antwort Stephen. Können Sie mir die TL; DR geben, warum eine wartende Task.WhenAll (...) Task.WaitAll () vorzuziehen ist? Können Sie mir außerdem einen Link zu einer Dokumentation bereitstellen, in der erläutert wird, wie APIs wie WPF und Windows Runtime Ereignishandler asynchron ausführen können?
Alex Marshall
1
Das Blockieren von Aufgaben kann zu einem Deadlock führen (Links zu meinem Blog). In Ihrem speziellen Fall wäre dies in Ordnung, da eine Konsolenmethode Maineine Ausnahme von der Richtlinie "Nicht blockieren" darstellt. Aber ich ziehe es vor, den außergewöhnlichen Code (mit Wait) von jeder Logik zu trennen , zu der ich gehe MainAsync. Es ist nur so viel weniger wahrscheinlich, dass Sie einen Deadlock erleiden, wenn Sie diesen Code in eine GUI- oder ASP.NET-App kopieren / einfügen.
Stephen Cleary
2
Asynchrone Ereignishandler nutzen den aktuellen Wert SynchronizationContext. WPF (und ich glaube WinRT) müssen nichts Besonderes tun; Ihr Vorhandensein SynchronizationContextreicht aus, damit sich asyncEvent-Handler korrekt verhalten. Ich erkläre die Erfassung / Wiederaufnahme des Kontexts in meinem Intro-Beitrag und habe auch einen MSDN-Artikel , der SynchronizationContextausführlich behandelt wird, wenn Sie ihn interessant finden.
Stephen Cleary
15

Verstehen Sie C # Task, asynchron und warten Sie

C # Aufgabe

Die Task-Klasse ist ein asynchroner Task-Wrapper. Thread.Sleep (1000) kann einen laufenden Thread für 1 Sekunde stoppen. Während Task.Delay (1000) die aktuelle Arbeit nicht stoppt. Siehe Code:

public static void Main(string[] args){
    TaskTest();
}
private static void TaskTest(){
     Task.Delay(5000);
     System.Console.WriteLine("task done");
}

Beim Ausführen wird sofort "Aufgabe erledigt" angezeigt. Ich kann also davon ausgehen, dass jede Methode aus Task asynchron sein sollte. Wenn ich TaskTest () durch Task.Run (() => TaskTest ()) ersetze, wird die erledigte Aufgabe erst angezeigt, wenn ich eine Console.ReadLine () anhänge. nach der Run-Methode.

Intern repräsentiert die Task-Klasse einen Thread-Status in einer Zustandsmaschine. Jeder Zustand in der Zustandsmaschine hat mehrere Zustände wie Start, Verzögerung, Abbrechen und Stopp.

asynchron und warten

Nun fragen Sie sich vielleicht, ob alle Aufgaben asynchron sind. Was ist der Zweck von Task.Delay? Lassen Sie uns als Nächstes den laufenden Thread mithilfe von Async wirklich verzögern und warten

public static void Main(string[] args){
     TaskTest();
     System.Console.WriteLine("main thread is not blocked");
     Console.ReadLine();
}
private static async void TaskTest(){
     await Task.Delay(5000);
     System.Console.WriteLine("task done");
}

async tell caller, ich bin eine asynchrone Methode, warte nicht auf mich. Warten Sie im TaskTest () und fragen Sie nach dem Warten auf die asynchrone Aufgabe. Nach dem Ausführen wartet das Programm nun 5 Sekunden, um den Text für die erledigte Aufgabe anzuzeigen.

Aufgabe abbrechen

Da Task eine Zustandsmaschine ist, muss es eine Möglichkeit geben, die Task abzubrechen, während die Task ausgeführt wird.

static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static void Main(string[] args){
    TaskTest();
    System.Console.WriteLine("main thread is not blocked");
    var input=Console.ReadLine();
    if(input=="stop"){
          tokenSource.Cancel();
          System.Console.WriteLine("task stopped");
     }
     Console.ReadLine();
}
private static async void TaskTest(){
     try{
          await Task.Delay(5000,tokenSource.Token);
     }catch(TaskCanceledException e){
          //cancel task will throw out a exception, just catch it, do nothing.
     }
     System.Console.WriteLine("task done");
}

Wenn das Programm ausgeführt wird, können Sie jetzt "stop" eingeben, um die Verzögerungsaufgabe abzubrechen.

Andrew Zhu
quelle
12

Ihre Aufgaben werden nie beendet, weil sie nie ausgeführt werden.

Ich würde Task.Factory.StartNeweine Aufgabe erstellen und starten.

public static async Task Task1()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
  Debug.WriteLine("Finished Task2");
}

Nebenbei bemerkt, wenn Sie wirklich nur versuchen, eine asynchrone Methode anzuhalten, müssen Sie nicht einen ganzen Thread blockieren, sondern nur verwenden Task.Delay

public static async Task Task1()
{
  await Task.Delay(TimeSpan.FromSeconds(5));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Delay(TimeSpan.FromSeconds(10));
  Debug.WriteLine("Finished Task2");
}
MerickOWA
quelle
1
Warum überhaupt neue Aufgaben erstellen?
David Heffernan
1
@ DavidHeffernan hatte noch keine Gelegenheit gehabt, das zu meiner Antwort hinzuzufügen, aber ich nahm an, dass Alex vielleicht etwas Komplizierteres im Sinn hat.
MerickOWA
@MerickOWA Ja, du hast recht, ich hatte etwas komplizierteres im Sinn. Ich wollte Threads simulieren, die E / A blockieren und lange dauern und nicht rein CPU-gebunden sind.
Alex Marshall
8

Async und Warten sind Markierungen, die Codepositionen markieren, von denen aus die Steuerung nach Abschluss einer Aufgabe (eines Threads) fortgesetzt werden soll. Hier ist ein detailliertes Youtube-Video, das das Konzept auf demonstrative Weise erklärt: http://www.youtube.com/watch?v=V2sMXJnDEjM

Wenn Sie möchten, können Sie auch diesen Artikel über das Coodeprojekt lesen, in dem dies auf visuellere Weise erklärt wird. http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features#Feature1:- "Async" und "Await" (Codemarkers)

Shivprasad Koirala
quelle