当我使用 ConfigureAwait(false) 时,为什么我的自定义当前调度程序会被默认调度程序替换?

Why is my custom current scheduler replaced by the default one when I use ConfigureAwait(false)?

我写了一个自定义 TaskScheduler,它应该在同一个线程上执行给定的任务。此任务计划程序与自定义任务工厂一起使用。此任务工厂执行一个异步方法 ReadFileAsync,该方法调用 StreamReader.

的另一个异步方法 ReadToEndAsync

我注意到在使用 ReadToEndAsync().ConfigureAwait(false) 后,当前的任务计划程序恢复为默认的 ThreadPoolTaskScheduler。如果我删除 ConfigureAwait(false),自定义任务计划程序 SameThreadTaskScheduler 将保留。为什么?有什么方法可以在执行后将 ConfigureAwait(false) 与相同的自定义调度程序一起使用?

我试过很多东西,但结果是一样的:

public static class Program
{
    private static readonly string DesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

    public static void Main()
    {
        _ = AsyncHelper.RunSynchronously(ReadFileAsync);
    }

    private static async Task<string> ReadFileAsync()
    {
        // Prints "SameThreadTaskScheduler"
        Console.WriteLine(TaskScheduler.Current.GetType().Name);

        using var fs = File.OpenText(Path.Combine(DesktopPath, "hello.txt"));
        var content = await fs.ReadToEndAsync().ConfigureAwait(false); // <-------- HERE

        // With ReadToEndAsync().ConfigureAwait(false), prints "ThreadPoolTaskScheduler"
        // With ReadToEndAsync() only, prints "SameThreadTaskScheduler"
        Console.WriteLine(TaskScheduler.Current.GetType().Name);

        return content;
    }
}

public static class AsyncHelper
{
    private static readonly TaskFactory SameThreadTaskFactory = new TaskFactory(
        CancellationToken.None,
        TaskCreationOptions.None,
        TaskContinuationOptions.None,
        new SameThreadTaskScheduler());

    public static TResult RunSynchronously<TResult>(Func<Task<TResult>> func)
    {
        var oldContext = SynchronizationContext.Current;

        try
        {
            SynchronizationContext.SetSynchronizationContext(null);
            return SameThreadTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(oldContext);
        }
    }
}

public sealed class SameThreadTaskScheduler : TaskScheduler
{
    public override int MaximumConcurrencyLevel => 1;

    protected override void QueueTask(Task task)
    {
        this.TryExecuteTask(task);
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        this.TryExecuteTask(task);
        return true;
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return Enumerable.Empty<Task>();
    }
}

ConfigureAwait(bool continueOnCapturedContext) 中的参数 continueOnCapturedContext 含义如下: 如果指定了 true ,这意味着继续应该被编组回捕获的原始上下文。如果指定 false,则延续可以 运行 在任意上下文中。

同步上下文是调度的抽象。 TaskScheduler是一个具体的实现。因此,通过指定 ConfigureAwait(false),您声明可以使用任何 TaskScheduler。如果您想使用特殊的 TaskScheduler,请使用 ConfigureAwait(true).

有关此主题的更多信息,请查看 this post