我有一个使用 TaskCompletionSource 包装回调的方法,如下所示:
public Task<int> TestMethod(int argument)
{
var tcs = new TaskCompletionSource<int>();
var task = tcs.Task;
LegacyMethodWithCallback(argument, (returnValue) => tcs.TrySetResult(returnValue));
return task;
}
然后我等待该方法:
async int CallingMethod()
{
var returnValue = await TestMethod(5);
Console.WriteLine(returnValue);
return returnValue;
}
编辑:LegacyMethodWithCallback 是一种使用网络与服务器通信的方法。回调在属于我们专有线程池的线程上运行(这是实现自定义 SynchronizationContext 的原因)。自定义 SynchronizationContext 基本上是空白的,除了它的 Post 方法将委托(delegate)排队到线程池中。以下是 SC 的实现,应演示何时调用 Post:
internal class ServiceSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
Console.WriteLine("Post " + d + ": " + state + " for " + this.serviceLogic.ServiceLogicId);
// Enqueue the delegate into our thread pool
}
}
我已经实现了自己的同步上下文,但我惊讶地发现 CallingMethod 似乎在调用 TrySetResult 的同一线程上立即恢复(因此也是调用回调的同一线程)。我最初期望通过 SynchronizationContext 发布延续。
此行为是否得到保证,或者是否存在调用 SynchronizationContext(或任何其他线程)的情况?如果这种行为不是完全确定性的,我可以以某种方式控制它吗?
我彻底检查了 CallingMethod 恢复时的调用堆栈,似乎甚至没有考虑 SynchronizationContext(但仍然有一些代码无法轻易查看),因此看来这种行为确实得到了保证。我说得对吗?
最佳答案
Is this behavior guaranteed or are there any cases in which the SynchronizationContext (or any other thread) is invoked?
SynchronizationContext(如果存在)始终由等待代码使用(除非 ConfigureAwait(false)
另有指定)。这意味着如果您无法控制等待代码,则不能假设等待代码将在您的线程上运行。
有几个原因导致你的延续不能内联在同一线程上。一是是否使用TaskCreationOptions.RunContinuationsAsynchronously。其他诸如当前的 SynchronizationContext
和 TaskScheduler
位于 IsValidLocationForInlining
中。 :
internal static bool IsValidLocationForInlining
{
get
{
// If there's a SynchronizationContext, we'll be conservative and say
// this is a bad location to inline.
var ctx = SynchronizationContext.CurrentNoFlow;
if (ctx != null && ctx.GetType() != typeof(SynchronizationContext)) return false;
// Similarly, if there's a non-default TaskScheduler, we'll be conservative
// and say this is a bad location to inline.
var sched = TaskScheduler.InternalCurrent;
return sched == null || sched == TaskScheduler.Default;
}
}
关于c# - TaskCompletionSource 是否保证等待代码将在调用 TCS.TrySetResult 的线程上恢复?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37695275/