c# - 在什么情况下 TaskCompletionSource.SetResult() 会同步运行延续?

标签 c# multithreading asynchronous async-await

最初我认为所有的延续都是在线程池上执行的(给定一个默认的同步上下文)。然而,当我使用 TaskCompletionSource 时,情况似乎并非如此。

我的代码看起来像这样:

Task<int> Foo() {
  _tcs = new TaskCompletionSource<int>();
  return _tcs.Task;
}

async void Bar() {
  Console.WriteLine(Thread.Current.ManagedThreadId);
  Console.WriteLine($"{Thread.Current.ManagedThreadId} - {await Foo()}");
}

Bar 在特定线程上被调用并且 TaskCompletionSource 保持未设置一段时间,这意味着返回的任务 IsComplete = false。然后一段时间后,同一个线程将继续调用 _tcs.SetResult(x),据我所知,这应该在线程池上运行延续。

但我在我的应用程序中观察到,运行延续的线程实际上仍然是同一个线程,就好像在调用 SetResult 时同步调用延续一样。

我什至尝试在 SetResult 上设置一个断点并跳过它(并在延续中有一个断点),这实际上又继续同步调用延续。

SetResult() 何时立即同步调用延续?

最佳答案

Initially I thought that all continuations are executed on the threadpool (given a default synchronization context). This however doesn't seem to be the case when I use a TaskCompletionSource.

实际上,当使用 await 时,大多数延续是同步执行的。

Marc 的回答很棒;我只是想更详细一点...

TaskCompletionSource<T>默认情况下将在 Set* 时同步运行叫做。 Set*将完成任务在单个方法调用中发出延续。 (这意味着在持有锁的同时调用 Set* 会导致死锁。)

我在那里使用了奇怪的短语“发布延续”,因为它可能会或可能不会实际执行它们;稍后会详细介绍。

TaskCreationOptions.RunContinuationsAsynchronously标志会告诉TaskCompletionSource<T>异步发出延续。这将完成任务(仍由 Set* 立即完成)与发出延续(仅由 Set* 调用触发)分开。所以用 RunContinuationsAsynchronously , 一个 Set* call只会完成任务;它不会同步执行延续。 (这意味着在持有锁的同时调用 Set* 是安全的。)

但回到默认情况,它会同步发出延续。

每个延续有一个标志;默认情况下,延续是异步执行的,但可以通过 TaskContinuationOptions.ExecuteSynchronously 使其同步。 . (请注意 await does use this flag - 链接指向我的博客;从技术上讲,这是一个实现细节,没有正式记录)。

然而,即使ExecuteSynchronously指定,有a number of situations where the continuation is not executed synchronously :

  • 如果有TaskScheduler与延续相关联,该调度程序可以选择拒绝当前线程,在这种情况下,任务排队TaskScheduler而不是同步执行。
  • 如果当前线程被中止,则任务在别处排队。
  • 如果当前线程的堆栈太深,则任务在别处排队。 (这只是一种启发式方法,并不能保证避免 StackOverflowException )。

这是相当多的条件,但通过简单的控制台应用测试,它们都得到了满足:

  • TaskCompletionSource<T>未指定 RunContinuationsAsynchronously .
  • 延续 ( await ) 确实指定了 ExecuteSynchronously .
  • 延续没有 TaskScheduler指定。
  • 目标线程能够继续执行(未中止;堆栈正常)。

作为一般规则,我会说 TaskCompletionSource<T> 的任何用法应指定 TaskCreationOptions.RunContinuationsAsynchronously .就我个人而言,我认为该标志的语义更合适,也不那么令人惊讶。

关于c# - 在什么情况下 TaskCompletionSource.SetResult() 会同步运行延续?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39296587/

相关文章:

Python奇怪的TypeError : stream_changes() takes exactly 1 argument (2 given)

node.js - NodeJs 数组中的项变量仅采用 for 循环中的第一个值

c# - 是否可以为接口(interface)引用定义 == 的行为?

c# - .NET 与 MySql.Data 的兼容性

python - 如何判断等待事件是否超时?

node.js - 使用 forEach 时与 Node.js 异步流程混淆。

python - PyPy 可以与 asyncio 一起使用吗?

c# - 通过 C# 中的循环发送电子邮件失败(使用 .net smtp)

c# - 多上下文内存数据库

java - 为什么 Goetz 不再在代码 list 7.20 中使用 volatile boolean 呢?