478 Stimmen

Wie ruft man sicher eine asynchrone Methode in C# auf, ohne await zu verwenden?

Ich habe eine async-Methode, die keine Daten zurückgibt:

public async Task MyAsyncMethod()
{
    // Führe einige Aufgaben asynchron aus, gebe keine Daten zurück
}

Ich rufe dies von einer anderen Methode aus auf, die einige Daten zurückgibt:

public string GetStringData()
{
    MyAsyncMethod(); // Dies erzeugt eine Warnung und unterdrückt Ausnahmen
    return "Hallo Welt";
}

Wenn MyAsyncMethod() aufgerufen wird, ohne darauf zu warten, wird in Visual Studio eine "Da dieser Aufruf nicht abgewartet wird, wird die aktuelle Methode weiterhin ausgeführt, bevor der Aufruf abgeschlossen ist"-Warnung angezeigt. Auf der Seite für diese Warnung heißt es:

Sie sollten die Warnung nur unterdrücken, wenn Sie sicher sind, dass Sie nicht auf das Abschließen des asynchronen Aufrufs warten möchten und dass die aufgerufene Methode keine Ausnahmen auslöst.

Ich bin mir sicher, dass ich nicht auf das Abschließen des Aufrufs warten möchte; Ich brauche es nicht oder habe keine Zeit dafür. Aber der Aufruf könnte Ausnahmen auslösen.

I ch bin ein paar Mal auf dieses Problem gestoßen, und ich bin sicher, dass es ein häufiges Problem ist, das eine gemeinsame Lösung haben muss.

Wie rufe ich sicher eine async-Methode auf, ohne auf das Ergebnis zu warten?

Aktualisierung:

Für Personen, die vorschlagen, dass ich einfach auf das Ergebnis warten soll, handelt es sich hier um Code, der auf eine Webanforderung auf unserem Webservice (ASP.NET Web-API) reagiert. Das Warten in einem UI-Kontext hält den UI-Thread frei, aber das Warten in einem Webanforderungsaufruf würde auf das Beenden des Tasks warten, bevor auf die Anforderung geantwortet wird, was die Antwortzeiten ohne Grund erhöht.

285voto

Peter Ritchie Punkte 34327

Wenn Sie die Ausnahme "asynchron" erhalten möchten, könnten Sie Folgendes tun:

  MyAsyncMethod().
    ContinueWith(t => Console.WriteLine(t.Exception),
        TaskContinuationOptions.OnlyOnFaulted);

Dies ermöglicht es Ihnen, mit einer Ausnahme auf einem anderen Thread als dem "Haupt"-Thread umzugehen. Das bedeutet, dass Sie nicht auf den Aufruf von MyAsyncMethod() von dem Thread aus warten müssen, der MyAsyncMethod aufruft; es ermöglicht Ihnen jedoch immer noch, etwas mit einer Ausnahme zu tun - aber nur, wenn eine Ausnahme auftritt.

Aktualisierung:

Technisch gesehen könnten Sie mit await etwas Ähnliches tun:

try
{
    await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
    Trace.WriteLine(ex);
}

...was nützlich wäre, wenn Sie speziell try/catch (oder using) verwenden müssten, aber ich finde ContinueWith etwas expliziter, weil Sie wissen müssen, was ConfigureAwait(false) bedeutet.

100voto

Stephen Cleary Punkte 402664

Du solltest zunächst erwägen, GetStringData zu einer async-Methode zu machen und es dazu bringen, auf die von MyAsyncMethod zurückgegebene Aufgabe zu await.

Wenn du absolut sicher bist, dass du keine Ausnahmen von MyAsyncMethod behandeln musst oder wissen musst, wann sie beendet ist, dann kannst du das tun:

public string GetStringData()
{
  var _ = MyAsyncMethod();
  return "hello world";
}

Übrigens, dies ist kein "übliches Problem". Es ist sehr selten, dass man etwas ausführen möchte und sich nicht darum kümmert, ob es und nicht darauf achten, ob es erfolgreich abgeschlossen wurde.

Aktualisierung:

Da du auf ASP.NET bist und frühzeitig zurückkehren möchtest, findest du meinen Blogbeitrag zu diesem Thema vielleicht nützlich. Allerdings wurde ASP.NET nicht für diesen Zweck entworfen, und es gibt keine Garantie, dass dein Code nach der Rückgabe der Antwort ausgeführt wird. ASP.NET wird sein Bestes tun, um es laufen zu lassen, aber es kann keine Garantie geben.

Daher ist dies eine gute Lösung für etwas Einfaches wie das Werfen eines Ereignisses in ein Protokoll, wo es nicht wirklich darauf ankommt, ob du hier und da ein paar verlierst. Es ist keine gute Lösung für betriebskritische Abläufe. In solchen Situationen muss man eine komplexere Architektur übernehmen, mit einem dauerhaften Weg, um die Operationen zu speichern (z.B. Azure Queues, MSMQ) und einem separaten Hintergrundprozess (z.B. Azure Worker Role, Win32 Service), um sie zu verarbeiten.

61voto

George Powell Punkte 8281

Die Antwort von Peter Ritchie war das, was ich wollte, und Stephen Clearys Artikel über das vorzeitige Beenden in ASP.NET war sehr hilfreich.

Als allgemeineres Problem jedoch (nicht spezifisch für einen ASP.NET-Kontext) demonstriert die folgende Konsolenanwendung die Verwendung und das Verhalten von Peters Antwort unter Verwendung von Task.ContinueWith(...)

static void Main(string[] args)
{
  try
  {
    // Ausgabe von "Hallo Welt", da die Methode früh zurückgegeben wird
    Console.WriteLine(GetStringData());
  }
  catch
  {
    // Die Ausnahme wird hier NICHT abgefangen
  }
  Console.ReadLine();
}

public static string GetStringData()
{
  MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
  return "Hallo Welt";
}

public static async Task MyAsyncMethod()
{
  await Task.Run(() => { throw new Exception("Auf dem Hintergrundthread geworfen"); });
}

public static void OnMyAsyncMethodFailed(Task task)
{
  Exception ex = task.Exception;
  // Behandeln Sie Ausnahmen hier so, wie Sie möchten
}

GetStringData() gibt früh zurück, ohne auf MyAsyncMethod() zu warten, und Ausnahmen, die in MyAsyncMethod() geworfen werden, werden in OnMyAsyncMethodFailed(Task task) behandelt und nicht im try/catch um GetStringData()

28voto

Filimindji Punkte 1423

Ich lande mit dieser Lösung:

public async Task MyAsyncMethod()
{
    // etwas async machen, keine Daten zurückgeben
}

public string GetStringData()
{
    // Async ausführen, keine Warnung, Ausnahmen werden abgefangen
    RunAsync(MyAsyncMethod()); 
    return "Hallo Welt";
}

private void RunAsync(Task task)
{
    task.ContinueWith(t =>
    {
        ILog log = ServiceLocator.Current.GetInstance();
        log.Error("Unerwarteter Fehler", t.Exception);

    }, TaskContinuationOptions.OnlyOnFaulted);
}

22voto

wast Punkte 730

Dies wird als fire and forget bezeichnet, und es gibt eine Erweiterung dafür.

Verbraucht eine Aufgabe und macht nichts damit. Nützlich für fire-and-forget-Aufrufe an asynchrone Methoden innerhalb von asynchronen Methoden.

Installiere das NuGet-Paket.

Verwendung:

MyAsyncMethod().Forget();

BEARBEITEN: Es gibt eine andere Möglichkeit, die ich in letzter Zeit eher verwendet habe:

_ = MyAsyncMethod();

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X