c# - 响应任务完成 : `.ContinueWith()` vs `GetAwaiter().OnCompleted()`

标签 c# task task-parallel-library continuations

假设我有一个 Task 生成 int ,以及接受 int 的回调:

Task<int> task = ...;
Action<int> f = ...;

现在我想设置它,以便一旦任务完成并返回其整数结果,callfack f将使用该整数调用 - 不需要主线程等待任务完成:
  • 据我了解,对此的典型解决方案是 Task.ContinueWith 方法:

    task.ContinueWith(t => f(t.Result));
    
  • 但是,您也可以获得 TaskAwaiter 对于任务,并使用其类似事件的界面:

    TaskAwaiter<int> awaiter = task.GetAwaiter();
    awaiter.OnCompleted(() => f(awaiter.GetResult()));
    

  • 现在,我意识到 TaskAwaiter不打算在应用程序代码中普遍使用,主要由 await 内部使用。关键词。
    但是为了加深我对TPL的理解,我想知道:

    解决方案(1)和(2)之间有什么实际区别吗?

    例如,
  • ...关于回调将在哪个线程上调用?
  • ...关于在任务或回调中抛出异常时会发生什么?
  • ……还有其他副作用吗?
  • 最佳答案

    一个区别是

    task.ContinueWith(t => f(t.Result));
    

    不会捕获当前的同步上下文,例如,在 UI 应用程序中 - 回调将在线程池线程上执行。尽管
    TaskAwaiter<int> awaiter = task.GetAwaiter();
    awaiter.OnCompleted(() => f(awaiter.GetResult()));
    

    将捕获同步上下文,并在 UI 线程上执行回调。

    当然你也可以用 ContinueWith 做同样的事情:
    task.ContinueWith(r => f(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
    

    但这不是你使用的问题。因此,您的问题中提供的方法至少在这方面有所不同。

    异常表示也有区别,访问Task.Result如果任务出错将抛出 AggregateException (有一个或多个异常作为内部异常),同时访问 awaiter.GetResult()不会在 AggregateException 中包装抛出的异常并将按原样重新抛出它(并且如果有多个异常,例如来自 Task.WhenAll - 除一个之外的所有异常都将被忽略,并且只会抛出一个)。

    关于c# - 响应任务完成 : `.ContinueWith()` vs `GetAwaiter().OnCompleted()` ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49921297/

    相关文章:

    flutter - 如何解决异常:Gradle任务assembleDebug失败,退出代码为1

    git - 创建 Git 标签时自动运行 Git 钩子(Hook)

    c# - WCF 双工中的 TPL 数据流 block

    c# - 如何在命令提示符下运行 selenium c# test?

    c# - 用颜色填充裁剪区域

    c# - 如何在 C# 中读取自定义文件属性

    c# - 自定义TaskFactory不使用自定义SynchronizationContext

    C# Directory Security SetAccessControl 没有生效?

    c# - 线程 C# 中的 Task.Delay

    f# - 如何在 F# 中使用 BlockingCollection<'a>.TryTake