最初我认为所有的延续都是在线程池上执行的(给定一个默认的同步上下文)。然而,当我使用 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/