你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

SyncAsyncEventHandler<T> 委托

定义

表示可以处理事件并同步或异步执行的方法。

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 

类型参数

T

派生或等于 SyncAsyncEventArgs的事件参数的类型。

参数

e
T

包含 SyncAsyncEventArgs 事件数据的实例。

返回值

表示处理程序的任务。 如果实现同步处理程序,则可以返回 CompletedTask 。 有关详细信息,请参阅“备注”部分。

示例

如果使用客户端的同步阻塞方法 (即没有 Async 后缀) 的方法,它们将引发需要处理程序同步执行的事件。 即使处理程序的签名返回 , Task也应编写常规同步代码,这些代码阻止并在完成后返回 CompletedTask

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
任务并行库中的异常处理 ,以避免未经处理的异常导致进程中断。 如果在从处理程序返回之前绝对需要执行异步方法,则可以调用 myAsyncTask.GetAwaiter().GetResult()。 请注意,这可能会导致 ThreadPool 耗尽。 有关更多详细信息,请参阅备注中的 sync-over-async 说明。

如果使用客户端的异步非阻塞方法 (即具有 Async 后缀) 的方法,它们将引发预期处理程序异步执行的事件。

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

await client.SnoozeAsync();

可以从同步和异步代码路径引发相同的事件,具体取决于你是在客户端上调用同步还是异步方法。 如果编写异步处理程序,但从同步方法引发它,该处理程序将执行异步同步,并可能导致 ThreadPool 耗尽。 有关更多详细信息,请参阅备注中的说明。 应使用 IsRunningSynchronously 属性检查引发事件的方式,并相应地实现处理程序。 下面是一个示例处理程序,可安全地从同步和异步代码路径调用。

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

注解

大多数用于 .NET 的 Azure 客户端库都提供用于调用 Azure 服务的同步和异步方法。 可以通过异步方法的 Async 后缀来区分这些异步方法。 例如,BlobClient.Download 和 BlobClient.DownloadAsync 进行相同的基础 REST 调用,只是在它们是否阻止方面有所不同。 建议对新应用程序使用异步方法,但使用同步方法也有完全有效的情况。 这些双方法调用语义提供了灵活性,但在编写事件处理程序时需要额外注意一点。

SyncAsyncEventHandler 是 Azure 客户端库中的事件用来表示可从同步或异步代码路径调用的事件处理程序的委托。 它采用派生自 SyncAsyncEventArgs 的事件参数,其中包含用于编写事件处理程序的重要信息:

  • CancellationToken 是与引发事件的原始操作相关的取消标记。 处理程序务必将此令牌传递给任何异步或长时间运行的同步操作,这些操作会获取令牌,以便取消 (, new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token例如,) 将正确传播。
  • IsRunningSynchronously 是一个标志,指示处理程序是同步调用还是异步调用。 如果要在客户端上调用同步方法,则应使用同步方法来实现事件处理程序, (可以返回 CompletedTask) 。 如果要在客户端上调用异步方法,则应尽可能使用异步方法来实现事件处理程序。 如果你无法控制客户端的使用方式,或者想要编写更安全的代码,则应检查 IsRunningSynchronously 属性,并按照指示调用同步或异步方法。
  • 大多数事件都会通过派生自 SyncAsyncEventArgs 并包括有关触发事件的内容的详细信息或提供响应选项来自定义事件数据。 很多时候,这将包括对引发事件的客户端的引用,以防需要它进行其他处理。

当引发使用 SyncAsyncEventHandler 的事件时,处理程序将按顺序执行,以避免引入任何意外的并行度。 在将控件返回到引发事件的代码路径之前,事件处理程序将完成。 这意味着对同步引发的事件进行阻止,并等待异步引发的事件返回 Task 完成。

从处理程序引发的任何异常都将包装在单个 AggregateException中。 如果一个处理程序引发异常,则不会阻止其他处理程序运行。 这也与取消相关,因为如果发生取消,仍会引发所有处理程序。 应同时传递到 CancellationToken 异步或长时间运行的同步操作,并考虑在计算密集型处理程序中调用 ThrowIfCancellationRequested()

分布式跟踪范围使用事件名称包装在处理程序周围,因此可以查看处理程序运行所花费的时间、是否对 Azure 服务进行了其他调用,以及有关引发的任何异常的详细信息。

从同步代码路径执行异步代码通常称为同步同步,因为你正在获取同步行为,但仍在调用所有异步机制。 请参阅 Diagnosing.NET Core ThreadPool Starvation with PerfView ,详细了解这会导致严重的性能问题。 建议使用 IsRunningSynchronously 标志来避免 ThreadPool 耗尽。

适用于