2016-05-20 15 views
0

WARNUNG Ich bin ein kompletter Neuling mit async/erwarten, und so missverstehen Sie wahrscheinlich dies vollständig!Warum läuft mein Async-Code nicht asynchron?

Ich versuche herauszufinden, wie das Zeug funktioniert, und versuchte ein einfaches bisschen Code in der Ansicht eines WPF-Fensters. Ich fügte hinzu, Event-Handler auf eine Schaltfläche klicken, und fügte einige sync und async Methoden wie folgt ...

public partial class MainWindow { 
    private Random _r = new Random(DateTime.Now.Millisecond); 

    public MainWindow() { 
    InitializeComponent(); 
    } 

    private async void Bleah_Click(object sender, RoutedEventArgs e) { 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    await DoStuffAsync(); 
    DoMoreStuffSync(); 
    AddToMsg("Done"); 
    } 

    private void DoSyncStuff() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoSyncStuff - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoSyncStuff - finished"); 
    } 

    private void DoMoreStuffSync() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoMoreStuffSync - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoMoreStuffSync - finished"); 
    } 

    private async Task DoStuffAsync() { 
    await Task.Run(() => { 
     int delay = _r.Next(500, 1500); 
     AddToMsg("DoStuffAsync - waiting for " + delay + "ms"); 
     Thread.Sleep(delay); 
     AddToMsg("DoStuffAsync - finished"); 
    }); 
    } 

    private void AddToMsg(string msg) { 
    Dispatcher.BeginInvoke(
     new Action(() => { LstMessages.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " - " + msg); })); 
    } 

LstMessages ein ListBox auf dem Fenster ist.

Wenn ich auf die Schaltfläche klicke, sehe ich, dass die drei Methoden immer in der Reihenfolge ausgeführt werden, in der ich sie anrufe, unabhängig von der Länge jeder Verzögerung.

Ich verstehe offensichtlich nicht, wie das Zeug funktioniert, aber nachdem ich einige Stunden lang herumgelesen bin und viele Variationen des Codes ausprobiert habe, kann ich nicht so funktionieren, wie ich es erwarte.

Bitte kann jemand klären, was ich hier falsch gemacht habe?

+0

Async nicht parallelisieren nicht Ihr Code es einfach nicht den aufrufenden Thread blockieren. – juharr

+1

Eine gute Ressource ist https://msdn.microsoft.com/en-us/magazine/jj991977.aspx - einige der Beispiele sollten zu Ihrem Fall sprechen – Brian

+2

Verwenden Sie 'Thread.Sleep' nicht in Ihrer asynchronen Methode. Verwenden Sie 'Task.Delay', damit Sie den Thread nicht blockieren. – mason

Antwort

1

Versuchen Sie diesen Ansatz, es scheint, dass Sie eine asynchrone Methode gestartet haben, aber sofort darauf im UI-Thread warten.

private async void Bleah_Click(object sender, RoutedEventArgs e) 
{ 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    Task t = DoStuffAsync(); 
    DoMoreStuffSync(); 
    await t; 
    AddToMsg("Done"); 
} 
+0

Danke, sowohl Sie als auch Will Ray haben mir geholfen, dies zu verstehen. Ich wünschte, ich könnte beide Antworten als Antworten markieren. Markieren Sie Ihre, wie es zuerst kam, und beantwortete meine Frage direkt –

0

Was Sie sehen, ist sinnvoll, da Sie alle Aktionen im Hauptthread der Benutzeroberfläche ausführen. Sie müssen entweder erstellen und Ihre eigenen Thread/Background Objekt verwalten, oder ein Verfahren zum

BackgroundWorker

Thread

ThreadPool

Jeder Ansatz seine eigenen Vor- und Nachteile hat, Thread vorzulegen, kann in anderen Antworten hier gefunden werden. Geben Sie diesen Links einen Lese- und probieren Sie die Beispiele aus

+0

Diese 3 Ansätze haben ziemlich viele Nachteile im Gegensatz zu nur mit async/erwarten Sie richtig mit Task.Run, siehe den Abschnitt "Threads" hier: https://msdn.microsoft.com/en-us/library/mt674882.aspx –

+0

@ thunder2709 Danke für die Antwort, aber ich weiß schon, wie man diese benutzt. Ich versuche zu lernen, wie man die neuen Funktionen verwendet –

1

Die wichtige Sache zu verstehen ist, dass async and await keywords don't cause additional threads to be created. (Task.Run() kann CAN-Arbeit in einen anderen Thread verschieben). Was passiert wirklich in deinem Code?

Also, in Ihrem Code, der erste Anruf an DoSyncStuff() pausiert den Hauptthread. Ihr Anruf an DoStuffAsync() wird nicht einmal ausgeführt, bis DoSyncStuff() vollständig abgeschlossen ist.

Ihr Anruf zu DoStuffAsync ausgelöst wird, als ob es async ist -, sondern weil Sie das await Schlüsselwort in der Anrufer Funktion ‚erwartet DoStuffAsync()‘, Steuer Haupt-Thread zum Bleah_Click() Anrufer zurückkehren wird (was für Ihre Zwecke nicht mach alles super interessant). Sobald DoStuffAsync() abgeschlossen ist, kehrt die Steuerung zu Bleah_Click zurück und DoMoreStuffSync() wird ausgeführt - was wiederum Ihren Haupt-Thread pausiert.

AS zu Ihrer Frage: Ich kann Ihnen nicht sagen, was Sie "falsch gemacht" haben, wie Sie nicht wirklich das gewünschte Ergebnis angegeben haben - wenn Sie die Ausführung des UIhrewp anhalten und alle Ihre Funktionen ausführen möchten Ordnung aufgelistet, dann hast du alles richtig gemacht.

2

Alles, was Sie tun müssen, ist das await Schlüsselwort in Ihrem Code fallen.

Um ein blog post von Eric Lippert zu zitieren:

Jedes Mal, wenn eine Aufgabe, „erwartet“, wird der Rest der aktuellen Methode als eine Fortsetzung der Aufgabe hat unterschrieben, und dann sofort an den Aufrufer zurückkehrt steuern . Wenn die Aufgabe abgeschlossen ist, wird die Fortsetzung aufgerufen, und die Methode beginnt dort, wo sie zuvor war.

im await Stichwort Durch das Hinzufügen, da sagen Sie effektiv „sobald dieses Asynchron-Verfahren abgeschlossen ist, mit dem Rest dieser Methode weitermachen“.

Es könnte einfacher sein, dies mit Methoden zu verstehen, die einen Wert zurückgeben. Das folgende Programm wird sofort zwei Methoden starten und await das Ergebnis der Async-Methode nach dem Aufruf der Synchronisierungsmethode. Sie können versuchen, die Zeile await zu verschieben, um den Unterschied im Verhalten zu beobachten.

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

    static async void MainAsync() 
    { 
     var task = GetNumberAsync(); 
     var syncNumber = GetNumber(); 
     var asyncNumber = await task; // moving this line above "GetNumber();" will make these run in order 

     Console.WriteLine(syncNumber); 
     Console.WriteLine(asyncNumber); 
    } 

    private static int GetNumber() 
    { 
     Console.WriteLine("DoSomeWork - started"); 
     Thread.Sleep(1000); 
     Console.WriteLine("DoSomeWork - finished"); 
     return 11; 
    } 

    private static async Task<int> GetNumberAsync() 
    { 
     Console.WriteLine("GetNumberAsync - started"); 
     await Task.Delay(1000); 
     Console.WriteLine("GetNumberAsync - finished"); 
     return 22; 

    } 
} 
+0

Große Antwort. Ich wünschte, ich könnte sowohl deine als auch Chris als Antworten markieren, da er meine Frage direkt beantwortete, während deine mehr Informationen gab. Bitte seien Sie nicht beleidigt, aber ich werde ihn wahrscheinlich als die Antwort markieren, wie sie zuerst kam. Danke noch einmal –