当我使用ConfigureAwait(false)时,为什么我的自定义当前调度器被默认调度器取代? &



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

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

我已经尝试了很多东西,但结果是一样的:

  • 修改自定义TaskFactory的枚举标志
  • 使用自定义同步上下文来同步Posts回调,而不是线程池
  • 更改和恢复在任务工厂上执行的任务函数内部的同步
  • 在任务工厂上执行的任务函数之外更改和恢复同步
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。如果你想使用你的特殊任务调度程序,请使用ConfigureAwait(true)

关于这个主题的更多信息,请看这篇文章。

最新更新