SyncAsyncEventHandler<T> Delegat

Definition

Stellt eine Methode dar, die ein Ereignis behandeln und entweder synchron oder asynchron ausgeführt werden kann.

public delegate System.Threading.Tasks.Task SyncAsyncEventHandler<T>(T e) where T : SyncAsyncEventArgs;
type SyncAsyncEventHandler<'T (requires 'T :> SyncAsyncEventArgs)> = delegate of 'T -> Task
Public Delegate Function SyncAsyncEventHandler(Of T)(e As T) As Task 

Typparameter

T

Typ der Ereignisargumente, die ableiten oder gleich sind SyncAsyncEventArgs.

Parameter

e
T

Eine SyncAsyncEventArgs instance, die die Ereignisdaten enthält.

Rückgabewert

Eine Aufgabe, die den Handler darstellt. Sie können zurückgeben CompletedTask , wenn Sie einen Synchronisierungshandler implementieren. Weitere Informationen finden Sie im Abschnitt Hinweise.

Beispiele

Wenn Sie die synchronen, blockierenden Methoden eines Clients (d. h. Methoden ohne asynchrones Suffix) verwenden, werden Ereignisse ausgelöst, die auch Handler synchron ausführen müssen. Obwohl die Signatur Ihres Handlers einen Taskzurückgibt, sollten Sie regulären Synchronisierungscode schreiben, der blockiert und nach Abschluss zurückgegeben CompletedTask wird.

var client = new AlarmClient();
client.Ring += (SyncAsyncEventArgs e) =>
{
    Console.WriteLine("Wake up!");
    return Task.CompletedTask;
};

client.Snooze();
   If you need to call an async method from a synchronous event handler,
   you have two options.  You can use <xref data-throw-if-not-resolved="true" uid="System.Threading.Tasks.Task.Run(System.Action)"></xref> to
   queue a task for execution on the ThreadPool without waiting on it to
   complete.  This "fire and forget" approach may not run before your
   handler finishes executing.  Be sure to understand
Ausnahmebehandlung in der Parallelen Taskbibliothek , um zu vermeiden, dass unbehandelte Ausnahmen Ihren Prozess abreißen. Wenn Sie unbedingt die asynchrone Methode ausführen müssen, bevor Sie von Ihrem Handler zurückgegeben werden, können Sie aufrufen myAsyncTask.GetAwaiter().GetResult(). Bitte beachten Sie, dass dies zu ThreadPool-Hunger führen kann. Weitere Informationen finden Sie im Hinweis "sync-over-async" unter Hinweise.

Wenn Sie die asynchronen, nicht blockierenden Methoden eines Clients (d. h. Methoden mit einem asynchronen Suffix) verwenden, werden Ereignisse ausgelöst, die erwarten, dass Handler asynchron ausgeführt werden.

var client = new AlarmClient();
client.Ring += async (SyncAsyncEventArgs e) =>
{
    await Console.Out.WriteLineAsync("Wake up!");
};

await client.SnoozeAsync();

Dasselbe Ereignis kann sowohl über synchrone als auch über asynchrone Codepfade ausgelöst werden, je nachdem, ob Sie Synchronisierungs- oder asynchrone Methoden auf einem Client aufrufen. Wenn Sie einen asynchronen Handler schreiben, ihn aber aus einer Synchronisierungsmethode auslösen, führt der Handler sync-over-async aus und kann threadPool-Verhungern verursachen. Weitere Informationen finden Sie im Hinweis unter Hinweise. Sie sollten die IsRunningSynchronously -Eigenschaft verwenden, um zu überprüfen, wie das Ereignis ausgelöst wird, und ihren Handler entsprechend implementieren. Hier sehen Sie einen Beispielhandler, der sicher sowohl von Synchronisierungs- als auch von asynchronen Codepfaden aufgerufen werden kann.

var client = new AlarmClient();
client.Ring += async (SyncAsyncEventArgs e) =>
{
    if (e.IsRunningSynchronously)
    {
        Console.WriteLine("Wake up!");
    }
    else
    {
        await Console.Out.WriteLineAsync("Wake up!");
    }
};

client.Snooze(); // sync call that blocks
await client.SnoozeAsync(); // async call that doesn't block

Hinweise

Die meisten Azure-Clientbibliotheken für .NET bieten synchrone und asynchrone Methoden zum Aufrufen von Azure-Diensten. Sie können die asynchronen Methoden durch ihr Asynchrones Suffix unterscheiden. Beispielsweise führen BlobClient.Download und BlobClient.DownloadAsync denselben zugrunde liegenden REST-Aufruf aus und unterscheiden sich nur darin, ob sie blockieren. Es wird empfohlen, unsere asynchronen Methoden für neue Anwendungen zu verwenden, aber es gibt auch völlig gültige Fälle für die Verwendung von Synchronisierungsmethoden. Diese Aufrufsemantik für duale Methoden ermöglicht Flexibilität, erfordert aber beim Schreiben von Ereignishandlern ein wenig zusätzliche Sorgfalt.

SyncAsyncEventHandler ist ein Delegat, der von Ereignissen in Azure-Clientbibliotheken verwendet wird, um einen Ereignishandler darzustellen, der entweder aus Synchronisierungs- oder asynchronen Codepfaden aufgerufen werden kann. Es werden ereignisbasierte SyncAsyncEventArgs Argumente benötigt, die wichtige Informationen zum Schreiben Ihres Ereignishandlers enthalten:

  • CancellationToken ist ein Abbruchtoken im Zusammenhang mit dem ursprünglichen Vorgang, der das Ereignis ausgelöst hat. Es ist wichtig, dass Ihr Handler dieses Token an alle asynchronen oder lang andauernden synchronen Vorgänge übergibt, die ein Token ausführen, damit der Abbruch (z. B. über etwas wie new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token) ordnungsgemäß weitergegeben wird.
  • IsRunningSynchronously ist ein Flag, das angibt, ob Ihr Handler synchron oder asynchron aufgerufen wurde. Wenn Sie Synchronisierungsmethoden auf Ihrem Client aufrufen, sollten Sie Synchronisierungsmethoden verwenden, um Ihren Ereignishandler zu implementieren (Sie können zurückgeben CompletedTask). Wenn Sie asynchrone Methoden auf Ihrem Client aufrufen, sollten Sie nach Möglichkeit asynchrone Methoden verwenden, um Ihren Ereignishandler zu implementieren. Wenn Sie nicht die Kontrolle darüber haben, wie der Client verwendet wird, oder wenn Sie sichereren Code schreiben möchten, sollten Sie die IsRunningSynchronously Eigenschaft überprüfen und entweder Synchronisierungs- oder asynchrone Methoden wie angegeben aufrufen.
  • Die meisten Ereignisse passen die Ereignisdaten an, indem sie von SyncAsyncEventArgs abgeleitet werden und Details dazu enthalten, was das Ereignis ausgelöst hat, oder indem Optionen zum Reagieren bereitgestellt werden. Häufig enthält dies einen Verweis auf den Client, der das Ereignis ausgelöst hat, falls Sie es für zusätzliche Verarbeitung benötigen.

Wenn ein Ereignis mit SyncAsyncEventHandler ausgelöst wird, werden die Handler sequenziell ausgeführt, um unbeabsichtigte Parallelität zu vermeiden. Die Ereignishandler werden abgeschlossen, bevor das Steuerelement an den Codepfad zurückgegeben wird, der das Ereignis auslöst. Dies bedeutet, dass für ereignisse blockiert wird, die synchron ausgelöst werden und auf den Abschluss der zurückgegebenen Ereignisse gewartet werden Task , die asynchron ausgelöst werden.

Alle Ausnahmen, die von einem Handler ausgelöst werden, werden in einem einzelnen AggregateExceptionumschlossen. Wenn ein Handler eine Ausnahme auslöst, wird nicht verhindert, dass andere Handler ausgeführt werden. Dies ist auch für den Abbruch relevant, da alle Handler weiterhin ausgelöst werden, wenn der Abbruch erfolgt. Sie sollten beide an asynchrone oder lang andauernde synchrone Vorgänge übergeben CancellationToken und das Aufrufen ThrowIfCancellationRequested() von Compute-Schwerhandlern in Erwägung ziehen.

Eine verteilte Ablaufverfolgungsspanne wird um Ihre Handler mithilfe des Ereignisnamens umschlossen, sodass Sie sehen können, wie lange ihre Handler für die Ausführung gedauert haben, ob sie andere Aufrufe von Azure-Diensten getätigt haben, und Details zu ausnahmen, die ausgelöst wurden.

Das Ausführen von asynchronem Code aus einem Synchronisierungscodepfad wird häufig als sync-over-async bezeichnet, da Sie Synchronisierungsverhalten erhalten, aber trotzdem alle asynchronen Maschinen aufrufen. Unter Diagnosing.NET Core ThreadPool Starvation mit PerfView finden Sie eine ausführliche Erläuterung, wie dies zu schwerwiegenden Leistungsproblemen führen kann. Es wird empfohlen, das IsRunningSynchronously Flag zu verwenden, um ThreadPool-Verhungerungen zu vermeiden.

Gilt für: