c# - TaskCompletionSource 是否保证等待代码将在调用 TCS.TrySetResult 的线程上恢复?

标签 c# .net async-await

我有一个使用 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。其他诸如当前的 SynchronizationContextTaskScheduler 位于 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/

相关文章:

c# - 如何实现惰性 TaskCompletionSource?

.net - 基于 TcpListener 的应用程序不能很好地扩展

c# - 在 System.Type 上使用条件断点时出错

c# - 如何将进程输出重定向到 System.String

c# - 带有表达式树的复杂构建器方法

c# - 如何在ado net中使用UPDATE

c# - 如何使用c#中的mongo驱动程序按条件排序

c# - 将 RTF 特殊字符输出到 Unicode

.net - CheckedListBox.Items 中每个项目的类型是什么?

javascript - 防止代码在 forEach 之后执行