当我使用 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)
与相同的自定义调度程序一起使用?
我试过很多东西,但结果是一样的:
- 更改自定义的枚举标志
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。如果您想使用特殊的 TaskScheduler,请使用 ConfigureAwait(true)
.
有关此主题的更多信息,请查看 this post。
我写了一个自定义 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。如果您想使用特殊的 TaskScheduler,请使用 ConfigureAwait(true)
.
有关此主题的更多信息,请查看 this post。