it-swarm.com.de

async/await - wann soll eine Aufgabe zurückgegeben werden?

Unter welchen Szenarien würde man verwenden wollen 

public async Task AsyncMethod(int num)

anstatt 

public async void AsyncMethod(int num)

Das einzige Szenario, das mir einfällt, ist, wenn Sie die Aufgabe benötigen, um den Fortschritt zu verfolgen. 

Sind in der folgenden Methode außerdem die asynchronen und wartenden Schlüsselwörter nicht erforderlich?

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}
373
user981225

1) Normalerweise möchten Sie eine Task zurückgeben. Die Hauptausnahme sollte sein, wenn einen Rückgabetyp void (für Ereignisse) haben muss. Wenn es keinen Grund gibt, den Anrufer await nicht für Ihre Aufgabe zuzulassen, warum sollten Sie dies nicht zulassen?

2) async-Methoden, die void zurückgeben, sind in einem anderen Aspekt besonders: Sie repräsentieren asynchrone Operationen der obersten Ebene und haben zusätzliche Regeln, die angewendet werden, wenn Ihre Task eine Ausnahme zurückgibt. Der einfachste Weg ist, den Unterschied anhand eines Beispiels zu zeigen:

static async void f()
{
    await h();
}

static async Task g()
{
    await h();
}

static async Task h()
{
    throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
    f();
}

private void button2_Click(object sender, EventArgs e)
{
    g();
}

private void button3_Click(object sender, EventArgs e)
{
    GC.Collect();
}

Die Ausnahme von f ist immer "beobachtet". Eine Ausnahme, die eine asynchrone Methode der obersten Ebene belässt, wird einfach wie jede andere nicht behandelte Ausnahme behandelt. Die Ausnahme von g wird niemals beachtet. Wenn der Garbage Collector die Aufgabe bereinigt, sieht er, dass die Aufgabe zu einer Ausnahme geführt hat und niemand die Ausnahme behandelt hat. In diesem Fall wird der TaskScheduler.UnobservedTaskException-Handler ausgeführt. Das solltest du niemals zulassen. Um Ihr Beispiel zu verwenden,

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}

Ja, verwenden Sie hier async und await. Sie stellen sicher, dass Ihre Methode weiterhin ordnungsgemäß funktioniert, wenn eine Ausnahme ausgelöst wird.

weitere Informationen finden Sie unter: http://msdn.Microsoft.com/de-de/magazine/jj991977.aspx

336
user743382

Ich bin auf diesen sehr nützlichen Artikel über async und void gestoßen, der von Jérôme Laban geschrieben wurde: http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and- Tricks-Teil-2-Async-void.aspx

Die Quintessenz ist, dass ein async+void das System zum Absturz bringen kann und normalerweise nur für Event-Handler der Benutzeroberfläche verwendet werden sollte.

Der Grund dafür ist der Synchronisationskontext, der von .__ verwendet wird. AsyncVoidMethodBuilder, in diesem Beispiel nicht vorhanden. Wenn es keine .__ gibt. Umgebungssynchronisationskontext, jede Ausnahme, die von .__ nicht behandelt wird. Der Body einer asynchronen Void-Methode wird im ThreadPool erneut angezeigt. Während Es gibt anscheinend keinen anderen logischen Ort, an dem diese Art von unbehandeltem Ausnahme kann ausgelöst werden, der unglückliche Effekt ist, dass der Prozess wird beendet, da nicht behandelte Ausnahmen im ThreadPool Beenden Sie den Prozess effektiv seit .NET 2.0. Sie können .__ abfangen. alle nicht behandelten Ausnahmen, die das Ereignis AppDomain.UnhandledException verwenden, Es gibt jedoch keine Möglichkeit, den Prozess von diesem Ereignis wiederherzustellen.

Beim Schreiben von UI-Ereignishandlern sind async-void-Methoden irgendwie schmerzlos, da Ausnahmen genauso behandelt werden wie in nicht asynchrone Methoden; Sie werden auf den Dispatcher geworfen. Da ist ein Möglichkeit, sich von solchen Ausnahmen zu erholen, ist mehr als richtig für die meisten Fälle. Außerhalb von UI-Ereignishandlern ist async jedoch ungültig Methoden sind irgendwie gefährlich und möglicherweise nicht so leicht zu finden.

30
Davide Icardi

Ich habe eine klare Vorstellung von diesen Aussagen.

  1. Async-Void-Methoden haben unterschiedliche Semantik der Fehlerbehandlung. Wenn eine Ausnahme aus einer asynchronen Task- oder asynchronen Taskmethode geworfen wird, wird diese Ausnahme erfasst und im Taskobjekt abgelegt. Bei asynchronen void-Methoden gibt es kein Task-Objekt. Daher werden alle Ausnahmen aus einer asynchronen void-Methode direkt im SynchronizationContext ausgelöst (SynchronizationContext stellt einen Ort dar, an dem der Code ausgeführt werden kann, an dem der async-void-Modus aktiv war.) gestartet

Ausnahmen von einer asynchronen Void Methode können nicht mit Catch erwischt werden

private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}

Diese Ausnahmen können mit AppDomain.UnhandledException oder einem ähnlichen Catch-All-Ereignis für GUI/ASP.NET-Anwendungen beobachtet werden. Die Verwendung dieser Ereignisse für die reguläre Ausnahmebehandlung ist jedoch ein Rezept für Unverwahrbarkeit (die Anwendung wird abgestürzt).

  1. Async-Void-Methoden haben unterschiedliche Komponentensemantiken. Async-Methoden, die Task oder Task zurückgeben, können einfach mit waitit, Task.WhenAny, Task.WhenAll usw. erstellt werden. Async-Methoden, die void zurückgeben, stellen keine einfache Möglichkeit dar, den aufrufenden Code zu benachrichtigen, dass sie abgeschlossen sind Es ist einfach, mehrere async-void-Methoden zu starten, aber es ist nicht leicht zu bestimmen, wann sie fertig sind. Async-void-Methoden benachrichtigen ihren SynchronizationContext beim Starten und Beenden. Ein benutzerdefinierter SynchronizationContext ist jedoch eine komplexe Lösung für regulären Anwendungscode.

  2. Async Void Methode nützlich bei der Verwendung von synchronen Ereignishandlern, da sie ihre Ausnahmen direkt im SynchronizationContext auslösen, ähnlich wie bei synchronen Ereignishandlern

Weitere Informationen finden Sie unter diesem Link https://msdn.Microsoft.com/en-us/magazine/jj991977.aspx

21

Das Problem beim Aufrufen von async void ist, dass Sie nicht einmal die Aufgabe zurückerhalten und nicht wissen können, wann die Aufgabe der Funktion abgeschlossen ist (siehe https://blogs.msdn.Microsoft.com/oldnewthing/ 20170720-00 /? P = 96655 )

Es gibt drei Möglichkeiten, eine asynchrone Funktion aufzurufen:

async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }

In allen Fällen wird die Funktion in eine Aufgabenkette umgewandelt. Der Unterschied ist, was die Funktion zurückgibt.

Im ersten Fall gibt die Funktion eine Task zurück, die schließlich das t erzeugt.

Im zweiten Fall gibt die Funktion eine Aufgabe zurück, die kein Produkt enthält, auf die Sie jedoch warten können, um zu erfahren, wann sie vollständig ausgeführt wurde.

Der dritte Fall ist der böse. Der dritte Fall ist wie der zweite, nur dass Sie die Aufgabe nicht einmal zurückerhalten. Sie können nicht wissen, wann die Aufgabe der Funktion abgeschlossen ist.

Der asynchrone Void-Fall ist ein "Feuer und Vergessen": Sie starten die Task-Kette, aber Sie kümmern sich nicht darum, wann sie beendet ist. Wenn die Funktion zurückkehrt, wissen Sie nur, dass alles bis zum ersten Warten ausgeführt wurde. Alles nach dem ersten Warten wird in der Zukunft an einem unbekannten Punkt ausgeführt, auf den Sie keinen Zugriff haben.

11
user8128167

Ich denke, Sie können async void auch zum Starten von Hintergrundoperationen verwenden, solange Sie vorsichtig sind, Ausnahmen zu erkennen. Gedanken?

class Program {

    static bool isFinished = false;

    static void Main(string[] args) {

        // Kick off the background operation and don't care about when it completes
        BackgroundWork();

        Console.WriteLine("Press enter when you're ready to stop the background operation.");
        Console.ReadLine();
        isFinished = true;
    }

    // Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
    static async void BackgroundWork() {
        // It's important to catch exceptions so we don't crash the appliation.
        try {
            // This operation will end after ten interations or when the app closes. Whichever happens first.
            for (var count = 1; count <= 10 && !isFinished; count++) {
                await Task.Delay(1000);
                Console.WriteLine($"{count} seconds of work elapsed.");
            }
            Console.WriteLine("Background operation came to an end.");
        } catch (Exception x) {
            Console.WriteLine("Caught exception:");
            Console.WriteLine(x.ToString());
        }
    }
}
5
bboyle1234

Meine Antwort ist einfach Sie warten auf die void-Methode Fehler CS4008 Kann 'void' TestAsync nicht erwarten? E:\test\TestAsync\TestAsync\Program.cs 

Wenn die Methode also asynchron ist, ist es besser, wartbar zu sein, da Sie den asynchronen Vorteil verlieren können.

0
Serg Sh