it-swarm.com.de

Wie und wann werden "asynchron" und "warten" verwendet?

Meines Verständnis nach ist eines der Hauptmerkmale von async UND await do, dass Code einfach zu schreiben und zu lesen ist - aber ist der Einsatz von ihnen gleich dem Hintergreifen von Hintergrundthreads, um eine Logik mit langer Dauer auszuführen?

Ich versuche gerade das grundlegendste Beispiel. Ich habe einige Kommentare inline hinzugefügt. Kannst du es mir erklären?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
830
Dan Dinu

Bei Verwendung von async und await erzeugt der Compiler im Hintergrund eine Zustandsmaschine.

Hier ist ein Beispiel, an dem ich hoffe, dass ich einige der übergeordneten Details erläutern kann: 

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

OK, was passiert hier also:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); beginnt mit der Ausführung von LongRunningOperation

  2. Unabhängige Arbeit wird gemacht, wenn der Haupt-Thread (Thread-ID = 1) angenommen wird und await longRunningTask erreicht wird. 

    Wenn longRunningTask noch nicht abgeschlossen ist und noch ausgeführt wird, kehrt MyMethodAsync() zu seiner aufrufenden Methode zurück, sodass der Haupt-Thread nicht blockiert wird. Wenn longRunningTask abgeschlossen ist, kehrt ein Thread aus dem ThreadPool (kann ein beliebiger Thread sein) in seinem vorherigen Kontext zu MyMethodAsync() zurück und setzt die Ausführung fort (in diesem Fall druckt das Ergebnis auf der Konsole). 

Ein zweiter Fall wäre, dass longRunningTask seine Ausführung bereits abgeschlossen hat und das Ergebnis verfügbar ist. Wenn wir await longRunningTask erreichen, haben wir bereits das Ergebnis, so dass der Code weiterhin in demselben Thread ausgeführt wird. (in diesem Fall Ergebnis auf Konsole ausgeben). Dies ist natürlich nicht der Fall für das obige Beispiel, an dem eine Task.Delay(1000) beteiligt ist. 

629
Dan Dinu

Weiter zu den anderen Antworten, schau nach waitit (C # Reference)

und genauer gesagt, am Beispiel erläutert es Ihre Situation ein wenig

Das folgende Windows Forms-Beispiel veranschaulicht die Verwendung von waitit in einer async-Methode, WaitAsynchronouslyAsync. Vergleichen Sie das Verhalten von diesem Methode mit dem Verhalten von WaitSynchronously. Ohne zu warten Operator, der auf eine Aufgabe angewendet wird, wird WaitSynchronly synchron ausgeführt trotz der Verwendung des asynchronen Modifizierers in seiner Definition und eines Aufrufs an Thread.Schlaf in seinem Körper.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
142
Adriaan Stander

Nach meinem Verständnis ist es eines der wichtigsten Dinge, die async und wait erwarten, dass Code einfach zu schreiben und zu lesen ist.

Sie sollen asynchronous -Code einfach schreiben und lesen lassen, ja.

Ist es das Gleiche wie das Erzeugen von Hintergrund-Threads, um eine Logik mit langer Dauer auszuführen?

Überhaupt nicht.

// Ich verstehe nicht, warum diese Methode als "async" markiert werden muss.

Das Schlüsselwort async aktiviert das Schlüsselwort await. Daher muss jede Methode, die await verwendet, als async markiert sein.

// Diese Zeile wird nach dem 5 Sekunden dauernden Ruhezustand der DoSomethingAsync () - Methode erreicht. Sollte es nicht sofort erreicht werden? 

Nein, da async-Methoden standardmäßig nicht in einem anderen Thread ausgeführt werden.

// Wird dies in einem Hintergrundthread ausgeführt?

Nein.


Sie finden meine async/await Intro Hilfreich. Die offiziellen MSDN-Dokumente sind auch ungewöhnlich gut (insbesondere der Abschnitt TAP ), und das async-Team gibt ein ausgezeichnetes FAQ aus.

122
Stephen Cleary

Erläuterung

Hier ist ein schnelles Beispiel für async/await auf hohem Niveau. Darüber hinaus gibt es noch viel mehr Details zu beachten.

Hinweis: Task.Delay(1000) simuliert die Arbeit für 1 Sekunde. Ich denke, es ist am besten, wenn man darauf wartet, dass eine externe Ressource auf eine Antwort wartet. Da unser Code auf eine Antwort wartet, kann das System die laufende Aufgabe zur Seite stellen und zurückkehren, wenn sie fertig ist. In der Zwischenzeit kann er andere Arbeiten an diesem Thread ausführen.

Im folgenden Beispiel macht der erste Block genau das. Alle Tasks (die Task.Delay-Zeilen) werden sofort gestartet und zur Seite gestellt. Der Code wird in der await a-Zeile angehalten, bis die Verzögerung von 1 Sekunde abgeschlossen ist, bevor zur nächsten Zeile übergegangen wird. Da b, c, d und e alle fast zur gleichen Zeit wie a ausgeführt wurden (aufgrund des Fehlens des Wartens), sollten sie in diesem Fall ungefähr zur gleichen Zeit enden.

Im folgenden Beispiel startet der second-Block eine Task und wartet darauf, dass diese beendet wird (dies ist die Funktion von await), bevor die nachfolgenden Tasks gestartet werden. Jede Wiederholung dauert 1 Sekunde. Die await pausiert das Programm und wartet auf das Ergebnis, bevor Sie fortfahren. Dies ist der Hauptunterschied zwischen dem ersten und dem zweiten Block.

Beispiel

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

AUSGABE:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Zusätzliche Informationen zu SynchronizationContext

Hinweis: Hier werden die Dinge für mich ein wenig neblig. Wenn ich also bei irgendetwas falsch bin, korrigieren Sie mich bitte und ich werde die Antwort aktualisieren. Es ist wichtig, ein grundlegendes Verständnis dafür zu haben, wie dies funktioniert, aber Sie können ohne Experten auskommen, solange Sie ConfigureAwait(false) nie verwenden, obwohl Sie wahrscheinlich einige Optimierungsmöglichkeiten verlieren werden.

Es gibt einen Aspekt, durch den das Async/Erwartungskonzept etwas schwieriger zu verstehen ist. Das ist die Tatsache, dass in diesem Beispiel dies alles in demselben Thread geschieht (oder zumindest in Bezug auf seinen SynchronizationContext derselbe Thread ist). Standardmäßig stellt await den Synchronisationskontext des ursprünglichen Threads wieder her, auf dem er ausgeführt wurde. In ASP.NET haben Sie beispielsweise einen HttpContext, der an einen Thread gebunden ist, wenn eine Anforderung eingeht. Dieser Kontext enthält Elemente, die für die ursprüngliche HTTP-Anforderung spezifisch sind, z. B. das ursprüngliche Request-Objekt, das Dinge wie Sprache, IP-Adresse, Header, Wenn Sie die Threads während der Verarbeitung etwas wechseln, wird möglicherweise versucht, Informationen aus diesem Objekt auf einem anderen HttpContext abzurufen, der katastrophal sein könnte. Wenn Sie wissen, dass Sie den Kontext nicht für irgendetwas verwenden, können Sie sich dafür entscheiden, sich nicht darum zu kümmern. Dadurch kann Ihr Code grundsätzlich in einem separaten Thread ausgeführt werden, ohne den Kontext mitzubringen.

Wie erreichen Sie das? Standardmäßig geht der Code await a; davon aus, dass Sie den Kontext erfassen und wiederherstellen möchten:

await a; //Same as the line below
await a.ConfigureAwait(true);

Wenn Sie zulassen möchten, dass der Hauptcode in einem neuen Thread ohne den ursprünglichen Kontext fortgesetzt wird, verwenden Sie einfach false anstelle von true, damit er weiß, dass er den Kontext nicht wiederherstellen muss.

await a.ConfigureAwait(false);

Nachdem das Programm angehalten wurde, setzt es potentiell in einem völlig anderen Thread mit einem anderen Kontext fort. Hier kommt die Leistungsverbesserung zustande - sie könnte in jedem verfügbaren Thread fortgesetzt werden, ohne den ursprünglichen Kontext wiederherstellen zu müssen, mit dem er begonnen hat.

Ist das Zeug verwirrend? Hölle ja! Kannst du es herausfinden? Wahrscheinlich! Sobald Sie die Konzepte verstanden haben, fahren Sie mit den Erklärungen von Stephen Cleary fort, die eher auf jemanden mit einem technischen Verständnis von async/await ausgerichtet sind.

116
Joe Phillips

Anzeigen der obigen Erklärungen in Aktion in einem einfachen Konsolenprogramm -

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Und die Ausgabe ist:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Somit,

  1. Main startet die Langzeitmethode über TestAsyncAwaitMethods. Das kehrt sofort zurück, ohne den aktuellen Thread anzuhalten, und wir sehen sofort die Meldung "Zum Beenden drücken"
  2. Währenddessen läuft die LongRunningMethod im Hintergrund. Nach Abschluss dieses Vorgangs greift ein anderer Thread von Threadpool diesen Kontext auf und zeigt die endgültige Nachricht an

Somit ist kein Thread blockiert. 

47
sppc42

Ich denke, Sie haben mit System.Threading.Thread.Sleep ein schlechtes Beispiel ausgewählt.

Der Punkt einer async-Task besteht darin, sie im Hintergrund ausführen zu lassen, ohne den Haupt-Thread zu sperren, z. B. eine DownloadFileAsync.

System.Threading.Thread.Sleep ist nicht etwas "wird gerade gemacht", es schläft nur und deshalb ist Ihre nächste Zeile nach 5 Sekunden erreicht ...

Lesen Sie diesen Artikel, ich denke es ist eine großartige Erklärung für async und await concept: http://msdn.Microsoft.com/en-us/library/vstudio/hh191443.aspx

38
Vnuk

Hier ist ein schnelles Konsolenprogramm, um es denjenigen, die folgen, klar zu machen. Die "TaskToDo" -Methode ist Ihre langlaufende Methode, die Sie asynchron machen möchten. Die Ausführung von Async wird von der TestAsync-Methode ausgeführt. Die Testschleifenmethode durchläuft nur die Aufgaben "TaskToDo" und führt sie asynchron aus. Sie können dies in den Ergebnissen sehen, da sie nicht von Run zu Run in der gleichen Reihenfolge abschließen. Sie werden dem Konsolen-UI-Thread gemeldet, sobald sie abgeschlossen sind. Simplistisch, aber ich denke, die simplen Beispiele bringen den Kern des Musters besser zum Ausdruck als ausführlichere Beispiele:

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

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
17
MarkWalls

Diese Antwort soll einige spezifische Informationen zu ASP.NET bereitstellen.

Durch die Verwendung von async/await im MVC-Controller ist es möglich, die Thread-Pool-Auslastung zu erhöhen und einen deutlich besseren Durchsatz zu erreichen, wie im nachstehenden Artikel erläutert.

http://www.asp.net/mvc/tutorials/mvc-4/verwendung-asynchroner-methoden-in-aspnet-mvc-4

In Webanwendungen, bei denen eine große Anzahl gleichzeitiger Anforderungen unter .__ angezeigt wird. Start oder hat eine unruhige Last (wobei die Parallelität plötzlich zunimmt), Wenn diese Web-Service-Aufrufe asynchron gemacht werden, erhöht sich die Reaktionsfähigkeit Ihrer Bewerbung. Eine asynchrone Anforderung nimmt die Gleiche Zeit wie eine synchrone Anforderung zu verarbeiten. Zum Beispiel, Wenn eine Anforderung einen Webdienstanruf durchführt, für den zwei Sekunden erforderlich sind. abgeschlossen, dauert die Anfrage zwei Sekunden, ob sie ausgeführt wird synchron oder asynchron. Während eines asynchronen Aufrufs jedoch Ein Thread wird nicht daran gehindert, auf andere Anforderungen zu antworten, während er wartet, bis die erste Anforderung abgeschlossen ist. Daher asynchron Anforderungen verhindern das Anfordern von Warteschlangen und das Wachstum des Threadpools, wenn viele gleichzeitige Anforderungen, die lang andauernde Operationen auslösen.

11
Lex Li

Alle Antworten hier verwenden Task.Delay () oder eine andere integrierte async-Funktion. Aber hier ist mein Beispiel, das keine dieser asynchronen Funktionen verwendet:

    // Starts counting to a large numbewr and then immediately displays message "i'm counting...". 
    // Then it waits for task to finish and displays "finished, press any key".
    static void asyncTest ()
    {
        Console.WriteLine("Started asyncTest()");
        Task<long> task = asyncTest_count();
        Console.WriteLine("Started counting, please wait...");
        task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
        //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
        Console.WriteLine("Finished counting.");
        Console.WriteLine("Press any key to exit program.");
        Console.ReadLine();
    }

    static async Task<long> asyncTest_count()
    {
        long k = 0;
        Console.WriteLine("Started asyncTest_count()");
        await Task.Run(() =>
        {
            long countTo = 100000000;
            int prevPercentDone = -1;
            for (long i = 0; i <= countTo; i++)
            {
                int percentDone = (int)(100 * (i / (double)countTo));
                if (percentDone != prevPercentDone)
                {
                    prevPercentDone = percentDone;
                    Console.Write(percentDone.ToString() + "% ");
                }

                k = i;
            }
        });
        Console.WriteLine("");
        Console.WriteLine("Finished asyncTest_count()");
        return k;
    }
10
Tone Škoda

Um ehrlich zu sein, denke ich immer noch, dass die beste Erklärung die Zukunft ist und auf der Wikipedia verspricht: http://en.wikipedia.org/wiki/Futures_and_promises

Die Grundidee ist, dass Sie über einen separaten Pool von Threads verfügen, die Aufgaben asynchron ausführen. Bei der Verwendung Das Objekt gibt jedoch das Versprechen ab, die Operation zu einem bestimmten Zeitpunkt auszuführen, und gibt Ihnen das Ergebnis, wenn Sie es anfordern. Dies bedeutet, dass es blockiert wird, wenn Sie das Ergebnis abfragen und nicht abgeschlossen sind, ansonsten jedoch im Thread-Pool ausgeführt wird.

Von dort aus können Sie die Dinge optimieren: Einige Operationen können asynchron implementiert werden, und Sie können Dinge wie Datei IO und Netzwerkkommunikation optimieren, indem Sie nachfolgende Anforderungen zusammenfassen und/oder neu ordnen. Ich bin mir nicht sicher, ob dies bereits im Aufgabenrahmen von Microsoft enthalten ist, aber wenn nicht, wäre dies eines der ersten Dinge, die ich hinzufügen würde.

Sie können die zukünftige Mustersorte tatsächlich mit Erträgen in C # 4.0 implementieren. Wenn Sie wissen möchten, wie es genau funktioniert, kann ich diesen Link empfehlen, der eine anständige Arbeit leistet: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Wenn Sie jedoch selbst anfangen, sich damit zu beschäftigen, werden Sie feststellen, dass Sie wirklich eine Sprachunterstützung benötigen, wenn Sie all die coolen Dinge tun möchten. Genau das hat Microsoft getan.

9
atlaste

Sehen Sie sich diese Geige https://dotnetfiddle.net/VhZdLU an (und verbessern Sie sie falls möglich), um ein einfache Konsolenanwendung auszuführen, das die Verwendung von Task, Task.WaitAll zeigt (), asynchron und wartet auf Operatoren im selben Programm. 

Diese Geige sollte Ihr Ausführungszykluskonzept klarer machen. 

Hier ist der Beispielcode

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Trace kommt vom Ausgabefenster: enter image description here

6
vibs2006

Für schnellstes Lernen ..

  • Verstehen Sie den Ablauf der Methodenausführung (mit einem Diagramm): 3 Minuten

  • Selbstbeobachtung der Fragen (um des Lernens willen): 1 min

  • Schnell durch Syntax Zucker: 5 Minuten

  • Teilen Sie die Verwirrung eines Entwicklers: 5 Minuten

  • Problem: Ändern Sie schnell eine reale Implementierung von normalem Code in asynchronen Code: 2 Minuten

  • Wohin als nächstes?

Verstehen Sie den Ablauf der Methodenausführung (mit einem Diagramm): 3 Minuten

In diesem Bild konzentrieren Sie sich nur auf # 6 enter image description here

Schritt 6: AccessTheWebAsync () hat keine Arbeit mehr, die es ohne ein Ergebnis von getStringTask erledigen kann. Daher verwendet AccessTheWebAsync einen wait-Operator, um seinen Fortschritt anzuhalten und dem Aufrufer die Kontrolle (Ausbeute) zurückzugeben. AccessTheWebAsync gibt eine Task (mit einem String-Rückgabewert) an den Aufrufer zurück. Die Aufgabe stellt ein Versprechen dar, ein Zeichenfolgenergebnis zu erzeugen. Aber wann kommt der Anruf zurück? nochmal einen 2. Anruf?

Der Aufrufer von AccessTheWebAsync () hat nur gewartet (es hätte einige interne Aufgaben erledigen und dann bei Bedarf gewartet). Der Anrufer wartet also auf AccessTheWebAsync, und AccessTheWebAsync wartet im Moment auf GetStringAsync.

Denken Sie daran, dass die Methode bereits zurückgegeben wurde und nicht erneut zurückgegeben werden kann (kein zweites Mal). Woher soll der Anrufer das wissen? Es geht um Aufgaben! Aufgabe wurde zurückgegeben. Auf die Aufgabe wurde gewartet (keine Methode, kein Wert). Der Wert wird in Task festgelegt. Der Aufgabenstatus wird auf "Abgeschlossen" gesetzt. Der Anrufer überwacht nur die Aufgabe. Weitere liest für später hier .

Frage Selbstbeobachtung um des Lernens willen: 1 min

Passen wir die Frage etwas an:

Wie und wann zu verwenden async und await Tasks?

Weil das Lernen Task automatisch die anderen 2 abdeckt. Zumindest um zu lernen. Dies ist natürlich eine Antwort auf Ihre Frage zu async und await.

Schnell durch Syntax Zucker: 5 Minuten

  • Vor der Konvertierung (Originalmethode)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • Eine weitere Task-ified-Methode zum Aufrufen der obigen Methode

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Haben wir schon Erwarten oder Asynchrones erwähnt? Nein. Wenn Sie die obige Methode aufrufen, erhalten Sie eine Aufgabe. Welche können Sie überwachen. Sie wissen bereits, was die Aufgabe zurückgibt. Eine Ganzzahl.

  • Das Aufrufen einer Aufgabe ist etwas schwierig. Rufen wir MethodTask () auf

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Wir warten darauf, dass die Aufgabe erledigt wird. Daher der await. Da wir wait verwenden, müssen wir async (obligatorisch) und MethodAsync mit 'Async' als Präfix (Codierungsstandard) verwenden. Weitere liest für später hier

Teilen Sie die Verwirrung eines Entwicklers: 5 Minuten

Ein Entwickler hat den Fehler gemacht, Task nicht zu implementieren, aber es funktioniert immer noch! Versuchen Sie, die Frage und nur die akzeptierte Antwort zu verstehen hier angegeben . Ich hoffe, Sie haben gelesen und vollständig verstanden. Ebenso ist es in unserem Beispiel viel einfacher, eine bereits erstellte MethodAsync() aufzurufen, als diese Methode mit einer Task (MethodTask()) selbst zu implementieren. Den meisten Entwicklern fällt es schwer, sich mit Tasks vertraut zu machen, während sie einen Code in einen asynchronen konvertieren.

Tipp: Versuchen Sie, eine vorhandene Async-Implementierung (wie MethodAsync oder ToListAsync) zu finden, um die Schwierigkeit auszulagern. Wir müssen uns also nur mit Async auseinandersetzen und warten (was einfach und dem normalen Code ziemlich ähnlich ist).

Problem: Schnelle Umstellung einer realen Implementierung von normalem Code auf Async-Betrieb: 2 Minuten

Die Codezeile, die unten in der Datenebene gezeigt wird, brach ab (viele Stellen). Weil wir einen Teil unseres Codes von .Net Framework 4.2 auf .Net Core aktualisiert haben. Wir mussten dies in 1 Stunde in der gesamten Anwendung beheben!

var myContract = query.Where(c => c.ContractID == _contractID).First();

kinderleicht!

  1. EntityFrameWork-Nuget (es hat QueryableExtensions)
  2. namespace = Microsoft.EntityFrameworkCore

code wurde so geändert

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Methodensignatur geändert von

    Contract GetContract(int contractnumber)

    zu

    async Task<Contract> GetContractAsync(int contractnumber)

  2. aufrufmethode wurde ebenfalls betroffen: GetContractAsync(123456); wurde als GetContractAsync(123456).Result; aufgerufen

  3. Wir haben es überall in 30 Minuten geändert!

Der Architekt sagte uns jedoch, wir sollten die EntityFrameWork-Bibliothek nicht nur für diesen Zweck verwenden. Hoppla! Theater! Dann haben wir eine benutzerdefinierte Task-Implementierung durchgeführt. Was du weißt wie. Immer noch einfach!

Wie geht es weiter? Es gibt ein wundervolles kurzes Video über Konvertieren von synchronen Aufrufen in asynchrone in ASP.Net Core , denn das ist wahrscheinlich die Richtung, in die man gehen würde, nachdem man dies gelesen hat.

5
Blue Clouds
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

Auf einer höheren Ebene:

1) Das async-Schlüsselwort aktiviert das Warten und das ist alles, was es tut. Async-Schlüsselwort führt die Methode nicht in einem separaten Thread aus. Die anfängliche f async-Methode wird synchron ausgeführt, bis sie auf eine zeitaufwändige Task wartet.

2) Sie können auf eine Methode warten, die Task oder Task vom Typ T zurückgibt. Sie können nicht auf async void method warten.

3) In dem Moment, in dem die Begegnungen des Haupt-Threads auf eine zeitaufwendige Aufgabe warten oder wenn die eigentliche Arbeit gestartet wird, kehrt der Haupt-Thread zum Aufrufer der aktuellen Methode zurück. 

4) Wenn der Haupt-Thread auf eine noch ausgeführte Aufgabe wartet, erwartet er nicht, wartet er darauf und kehrt zum Aufrufer der aktuellen Methode zurück. Auf diese Weise bleibt die Anwendung ansprechbar.

5) Warten auf Verarbeitungstask, wird jetzt in einem vom Thread-Pool separaten Thread ausgeführt.

6) Wenn diese Aufwartungsaufgabe abgeschlossen ist, wird der gesamte darunterliegende Code vom separaten Thread ausgeführt

Unten ist der Beispielcode. Führen Sie es aus und überprüfen Sie die Thread-ID

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
3
ABajpai

So wie ich es verstehe, sollte dem Mix ein dritter Begriff hinzugefügt werden: Task.

Async ist nur ein Qualifikationsmerkmal, das Sie Ihrer Methode hinzufügen, um anzugeben, dass es sich um eine asynchrone Methode handelt.

Task ist die Rückgabe der Funktion async. Es wird asynchron ausgeführt.

Sie await eine Aufgabe. Wenn die Codeausführung diese Zeile erreicht, springt die Steuerung zum Aufrufer Ihrer umgebenden Originalfunktion zurück.

Wenn Sie stattdessen die Rückgabe einer async-Funktion (dh Task) einer Variablen zuweisen, wird die Code-Ausführung in dieser Zeile nur continue in der umgebenden Funktion fortgesetzt, während der Task asynchron ausgeführt wird.

2
user21306

Im folgenden Code gibt die HttpClient-Methode GetByteArrayAsync einen Task getContentsTask zurück. Die Aufgabe ist ein Versprechen, das tatsächliche Byte-Array zu erstellen, wenn die Aufgabe abgeschlossen ist. Der wait-Operator wird auf getContentsTask angewendet, um die Ausführung in SumPageSizesAsync auszusetzen, bis die getContentsTask abgeschlossen ist. In der Zwischenzeit wird die Kontrolle an den Aufrufer von SumPageSizesAsync zurückgegeben. Wenn getContentsTask abgeschlossen ist, wird der wait-Ausdruck zu einem Byte-Array ausgewertet.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
1
lazydeveloper

Async/Warten

Eigentlich sind Async/Await ein Schlüsselwortpaar, das nur syntaktischen Zucker zum Erstellen eines Rückrufs einer asynchronen Aufgabe darstellt.

Nehmen Sie diese Operation am Beispiel:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Diese Methode hat mehrere Nachteile. Fehler werden nicht weitergegeben und es ist extrem schwer zu lesen ... Aber Async und Await helfen uns:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Warten Sie, dass Aufrufe in asynchronen Methoden ausgeführt werden müssen. Das hat einige Vorteile:

  • Gibt das Ergebnis der Aufgabe zurück
  • erstellt automatisch einen Rückruf
  • prüft auf Fehler und lässt sie im Callstack aufblähen (nur bis zu nicht erwarteten Anrufen im Callstack)
  • wartet auf das Ergebnis
  • macht den Haupt-Thread frei
  • führt den Callback im Hauptthread aus
  • verwendet einen Arbeitsthread aus dem Threadpool für die Aufgabe
  • macht den Code leicht lesbar
  • und vieles mehr

NOTE: Async und Await werden with asynchrone Aufrufe not verwendet, um diese auszuführen. Sie müssen Task Libary verwenden, wie Task.Run ().

Hier ist ein Vergleich zwischen erwarteten und keinen Lösungen

Dies ist die nicht asynchrone Lösung:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        Thread.Sleep(1000);
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Dies ist die asynchrone Methode:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Sie können eine asynchrone Methode tatsächlich ohne das Schlüsselwort await aufrufen. Das bedeutet, dass hier alle Ausnahmen im Freigabemodus verschluckt werden:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async und Await sind nicht für paralleles Computing gedacht. Sie werden verwendet, um Ihren Haupt-Thread nicht zu blockieren. Wenn es um asp.net oder Windows-Anwendungen geht. Das Blockieren Ihres Haupt-Threads aufgrund eines Netzwerkanrufs ist eine schlechte Sache. Wenn Sie dies tun, reagiert Ihre App nicht mehr oder stürzt ab.

Check out ms docs um einige Beispiele zu erhalten.

1
Hakim

verwendet sie, um Hintergrund-Threads zu erzeugen, um lange Dauerlogik?

Dieser Artikel MDSN: Asynchrones Programmieren mit async und waitit (C #) erklärt es explizit:

Die Schlüsselwörter async und waiting erwarten nicht, dass zusätzliche Threads .__ sind. erstellt. Async-Methoden erfordern kein Multithreading, da ein asynchroner Methode läuft nicht in einem eigenen Thread. Die Methode wird auf dem aktuellen .__ ausgeführt. Synchronisationskontext und verwendet Zeit nur für den Thread, wenn die Methode ist aktiv.

1
Dmitry G.

Im Folgenden sind die Daumenregeln aufgeführt, nach denen ich entscheide, wann Versprechen verwendet und wann Async verwendet werden soll

  1. Die asynchrone Funktion gibt ein Versprechen zurück. Das Gegenteil ist auch wahr. Jede Funktion, die ein Versprechen zurückgibt, kann als asynchrone Funktion "Warten" betrachtet werden. Sie wird zum Aufrufen einer asynchronen Funktion verwendet und wartet, bis sie aufgelöst oder zurückgewiesen wird.

  2. wait blockiert die Ausführung des Codes innerhalb der asynchronen Funktion, in der er sich befindet. Wenn die Ausgabe von function2 von der Ausgabe von function1 abhängt, verwende ich await.

  3. Wenn zwei Funktionen parallel ausgeführt werden können, erstellen Sie zwei verschiedene asynchrone Funktionen und führen Sie sie dann parallel aus.

  4. Um Versprechungen parallel auszuführen, erstellen Sie eine Reihe von Versprechungen und verwenden Sie dann Promise.all (promisesArray). Denken Sie bei jeder Verwendung von wait daran, dass Sie blockierenden Code schreiben. Im Laufe der Zeit neigen wir dazu, dies zu vernachlässigen.

  5. Anstatt große asynchrone Funktionen mit vielen erwarteten asyncFunction () zu erstellen, ist es besser, kleinere asynchrone Funktionen zu erstellen. Auf diese Weise wird uns bewusst, dass wir nicht zu viel blockierenden Code schreiben. Ein weiterer Vorteil der Verwendung kleinerer asynchroner Funktionen besteht darin, dass Sie sich überlegen müssen, welche asynchronen Funktionen parallel ausgeführt werden können.

  6. Wenn Ihr Code blockierenden Code enthält, ist es besser, ihn asynchron zu machen. Auf diese Weise stellen Sie sicher, dass jemand anderes Ihre Funktion asynchron verwenden kann.

  7. Indem Sie aus blockierendem Code asynchrone Funktionen machen, ermöglichen Sie dem Benutzer, der Ihre Funktion aufruft, zu entscheiden, welchen Grad an Asynhronie er wünscht.

Ich hoffe, dies hilft Ihnen bei der Entscheidung, wann Sie Versprechungen verwenden und wann Sie Versprechungen verwenden und wann Sie eine Synchronisierung durchführen müssen

0
Sunny Sultan

Nachfolgend finden Sie Code, der die Excel-Datei liest, indem er den Dialog öffnet und dann asynchron verwendet. Warten Sie, um den Code asynchron auszuführen, der eine Zeile nacheinander aus Excel liest und an das Raster bindet 

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill Excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
0
Zaheer Abbas

Async & Warten auf einfache Erklärung

Einfache Analogie

Eine Person kann warten auf ihren Morgenzug. Dies ist alles, was sie tun, da dies ihre Hauptaufgabe ist, die sie gerade ausführen. (Synchronprogrammierung (was Sie normalerweise tun!))

Eine andere Person kann erwarten ihren Morgenzug, während sie eine Zigarette raucht und dann ihren Kaffee trinkt. (Asynchrone Programmierung)

Was ist asynchrone Programmierung?

Bei der asynchronen Programmierung wird ein Programmierer einen Teil seines Codes in einem separaten Thread vom Haupt-Thread der Ausführung ausführen und den Haupt-Thread darüber informieren, wenn er abgeschlossen ist.

Was macht das async-Keyword eigentlich?

Das async-Schlüsselwort sollte einem Methodennamen vorangestellt werden 

async void DoSomething(){ . . .

ermöglicht dem Programmierer, beim Aufrufen asynchroner Tasks das await-Schlüsselwort zu verwenden. Das ist alles was es tut.

Warum ist das wichtig?

In vielen Softwaresystemen ist der Haupt-Thread für Operationen reserviert, die sich speziell auf die Benutzeroberfläche beziehen. Wenn ich einen sehr komplexen rekursiven Algorithmus verwende, der auf meinem Computer 5 Sekunden dauert, aber auf dem Haupt-Thread (UI-Thread) ausgeführt wird. Wenn der Benutzer auf etwas in meiner Anwendung klickt, scheint er eingefroren zu sein Da mein Haupt-Thread in die Warteschlange gestellt wurde und gerade viel zu viele Operationen verarbeitet. Daher kann der Haupt-Thread den Mausklick nicht verarbeiten, um die Methode mit dem Klicken der Schaltfläche auszuführen.

Wann verwendest du Async und Await?

Verwenden Sie die asynchronen Schlüsselwörter ideal, wenn Sie etwas tun, das die Benutzeroberfläche nicht betrifft. 

Nehmen wir an, Sie schreiben ein Programm, mit dem der Benutzer auf seinem Mobiltelefon skizzieren kann, aber alle 5 Sekunden wird das Wetter im Internet überprüft.

Wir sollten darauf warten, dass die Abrufanrufe alle 5 Sekunden an das Netzwerk gesendet werden, um das Wetter zu ermitteln, da der Benutzer der Anwendung ständig mit dem mobilen Touchscreen interagieren muss, um schöne Bilder zu zeichnen.

Wie verwendet man Async und Await

Aus dem obigen Beispiel folgt ein Pseudo-Code, wie er geschrieben wird:

     //ASYNCHRONOUS
    //this is called every 5 seconds
    async void CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }
0
James Mallon

Die Antworten hier sind als allgemeine Anleitung zu wait/async nützlich. Sie enthalten auch einige Details zur Verkabelung von wait/async. Ich möchte einige praktische Erfahrungen mit Ihnen teilen, die Sie kennen sollten, bevor Sie dieses Entwurfsmuster verwenden.

Der Begriff "Warten" ist wörtlich, sodass jeder Thread, den Sie aufrufen, auf das Ergebnis der Methode wartet, bevor Sie fortfahren. Im Vordergrund-Thread ist dies ein Disaster. Der Vordergrund-Thread trägt die Last, Ihre App zu erstellen, einschließlich Ansichten, Ansichtsmodellen, anfänglichen Animationen und allem, was Sie sonst noch mit diesen Elementen gebootet haben. Wenn Sie also auf den Vordergrund-Thread warten, können Sie Stopp die App. Der Benutzer wartet und wartet, wenn nichts zu geschehen scheint. Dies bietet eine negative Benutzererfahrung.

Sie können einen Hintergrund-Thread mit verschiedenen Mitteln erwarten:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Der vollständige Code für diese Anmerkungen lautet https://github.com/marcusts/xamarin-forms-annoyances . Siehe die Lösung mit dem Namen AwaitAsyncAntipattern.sln.

Die GitHub-Site enthält auch Links zu einer ausführlicheren Diskussion zu diesem Thema.

0
Stephen Marcus