C# Result 和 ContinueWith 之间的区别

C# Differences between Result and ContinueWith

这两种看似做同样事情的方法有什么区别? async/await也能做到吗?

public Task<int> TaskMaxAsync1 ( Task<int>[] my_ints )
{
    return Task.WhenAll( my_ints )
    .ContinueWith ( x => x.Result.Where ( i => i%2 != 0 ).Max( ) ) ;
}

public Task<int> TaskMaxAsync2 ( Task<int>[] my_ints )
{
    var numbers = Task.WhenAll( my_ints ).Result ;
    return Task.FromResult( numbers.Where( i => i%2 != 0 ).Max( ) ) ;
}

What are the differences between this two methods that seems to do the same thing?

区别在于前者returns 是调用者的热点任务,而后者同步阻塞,然后使用Task.FromResult 将结果重新包装在Task 中。如果您 运行 在具有自定义 SynchronizationContext.

的环境中,后者也是 死锁 的常见情况

Can it be done even with async/await?

是的,它可以:

public async Task<int> MaxAsync(Task<int>[] myInts)
{
    int[] results = await Task.WhenAll(myInts);
    return results.Max(i => i % 2 != 0 ? i : (int?)null) ?? 0;
}

task0.Result 将同步阻塞并等待 task0 完成,而 task1.ContinueWith 不会等待 task1 完成,而是 return 一个新的 Task(当 task1 完成时将 运行)立即。

因此,您示例中的两个方法的行为并不相同。在您的第二种方法中,如果 my_ints 在传递给 WhenAll 方法时并非全部 运行 完成,则 .Result 将同步阻塞调用线程,无论时间长短my_ints 中的所有任务都需要完成,如果其中一些任务抛出异常,TaskMaxAsync2 抛出。

然而,第一个方法将 return 立即甚至 my_ints 永远不会完成或抛出异常。

Differences between Result and ContinueWith

Result 将同步阻塞直到任务完成,并将异常包装在 AggregateException.

ContinueWith 将向任务注册一个回调,并在任务完成时调用该回调。

对于异步代码,ResultContinueWith 都应替换为 await

Can it be done even with async/await?

当然,像这样:

public async Task<int> MaxAsync(Task<int>[] my_ints)
{
  int[] ints = await Task.WhenAll(my_ints);
  return ints.Where(i => i % 2 != 0).Max();
}