操作是否异步?

Zol*_*yak 1 c# action asynchronous async-await

假设我们有一个像这样的动作执行包装器:

private void ExecuteAction(Action action)
{
    Console.WriteLine($"starting action ...");
    action();
    Console.WriteLine($"finished action ...");
}
Run Code Online (Sandbox Code Playgroud)

假设我们这样称呼它:

ExecuteAction( async () => { ... } ); // 1
Run Code Online (Sandbox Code Playgroud)

或者像这样:

ExecuteAction( () => { ... } ); // 2
Run Code Online (Sandbox Code Playgroud)

我想知道这两个调用有什么区别以及ExecuteAction()方法内部如何处理?有什么区别吗?

Fil*_*dor 5

在这里使用Action委托将转化为async void众所周知的反模式(有例外)。

请注意,async关键字不是签名的一部分。await它是一个实现细节,允许在方法内部使用。

所以,ExecuteAction这没有什么区别。它看到的只是一个void Action()代表。使用它就像ExecuteAction( async () => { await SomethingAsync(); });我在上面链接的文章中列出的所有陷阱和缺点一样。(tl;dr:真的不建议这样做)

另请参阅有关关键字本身的文档:

空白。通常不建议将 async void 方法用于事件处理程序以外的代码,因为调用者无法等待这些方法,并且必须实现不同的机制来报告成功完成或错误条件。

-异步 - C# 参考

如果您想向观众提供同步和异步体验,您可以执行以下操作:

// your already known implementation
private void ExecuteAction(Action action)
{
  // ...
}

/// <summary>Async Version</summary>
/// <example>
/// Usage:
/// <code>
/// await ExecuteAsync( () => { ... } );
/// </code>
/// or
/// <code>
/// await ExecuteAsync( async () => { ... await SomethingAsync(); ... } );
/// </code>
/// </example>
private async Task ExecuteAsync(Func<Task> asyncAction)
{
    // Before client-defined function code goes here
    await asyncAction();
    // After client-defined function code goes here
}
Run Code Online (Sandbox Code Playgroud)

如果需要,您还可以传递一个状态对象并避免它被捕获:

/// <example>
/// <code>
/// await ExecuteAsync( o => { ... }, someStateObject );
/// </code>
/// </example>
private async Task ExecuteAsync(Func<object,Task> asyncAction, object state = null)
{
    await asyncAction(state);
}
Run Code Online (Sandbox Code Playgroud)

提供异步 API,我也总是有机会传递CancellationToken

/// <example>
/// <code>
/// await ExecuteAsync( o => { ... }, someStateObject, cancel );
/// </code>
/// </example>
private async Task ExecuteAsync(Func<object, CancellationToken, Task> asyncAction, object state = null, CancellationToken token = default)
{
    await asyncAction(state, token);
}
Run Code Online (Sandbox Code Playgroud)

如果这与 GUI 相关,您还可以考虑报告进度:

/// <example>
/// <code>
/// await ExecuteAsync( o => { ... }, someStateObject, p => { ... },cancel );
/// </code>
/// </example>
private async Task ExecuteAsync<TProgress>(
     Func<object, IProgress<TProgress>, CancellationToken, Task> asyncAction,
     object state = null, 
     IProgress<TProgress> progress = null,
     CancellationToken token = default)
{
    await asyncAction(state, progress, token);
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以使状态对象类型安全

private async Task ExecuteAsync<TState, TProgress>(
     Func<TState, IProgress<TProgress>, CancellationToken, Task> asyncAction,
     TState state = null, 
     IProgress<TProgress> progress = null,
     CancellationToken token = default)
{
    await asyncAction(state, progress, token);
}
Run Code Online (Sandbox Code Playgroud)

资料来源:

Async/Await - 异步编程的最佳实践 # 避免 Async Void - Stephen Cleary,2013 年 3 月,MSDN 杂志

异步 - C# 参考

  • 确切地。您只需以不同的方式命名它们,因为它们具有不同的返回类型。但是,是的:同步一件事,异步一事。 (2认同)