Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当我使用ConfigureWait(false)时,为什么我的自定义当前计划程序被默认计划程序替换?_C#_.net_Asynchronous_Async Await_Taskscheduler - Fatal编程技术网

C# 当我使用ConfigureWait(false)时,为什么我的自定义当前计划程序被默认计划程序替换?

C# 当我使用ConfigureWait(false)时,为什么我的自定义当前计划程序被默认计划程序替换?,c#,.net,asynchronous,async-await,taskscheduler,C#,.net,Asynchronous,Async Await,Taskscheduler,我编写了一个定制的TaskScheduler,它应该在同一个线程上执行给定的任务。此任务计划程序与自定义任务工厂一起使用。此任务工厂执行一个异步方法ReadFileAsync,该方法调用streamreadreader的另一个异步方法ReadToEndAsync 我注意到,在使用ReadToEndAsync().ConfigureAwait(false)后,当前任务计划程序将恢复为默认任务计划程序ThreadPoolTaskScheduler。如果删除ConfigureAwait(false),

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

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

我尝试了多种方法,但结果都是一样的:

  • 更改自定义任务工厂的枚举标志
  • 使用一个自定义的同步上下文,
    同步发布
    回调,而不是线程池
  • 更改并恢复在任务工厂上执行的任务函数内的同步
  • 在任务工厂上执行的任务功能之外更改并恢复同步
公共静态类程序
{
私有静态只读字符串DesktopPath=Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
公共静态void Main()
{
_=AsyncHelper.RunSynchronously(ReadFileAsync);
}
私有静态异步任务ReadFileAsync()
{
//打印“SameThreadTaskScheduler”
Console.WriteLine(TaskScheduler.Current.GetType().Name);
使用var fs=File.OpenText(Path.Combine(desktopath,“hello.txt”);
var content=await fs.ReadToEndAsync().ConfigureAwait(false);//1;
受保护的覆盖无效队列任务(任务任务)
{
this.TryExecuteTask(任务);
}
受保护的覆盖bool TryExecuteTaskInline(任务任务,bool任务先前排队)
{
this.TryExecuteTask(任务);
返回true;
}
受保护的重写IEnumerable GetScheduledTasks()
{
返回可枚举的.Empty();
}
}

配置等待(bool continueOnCapturedContext)中的参数
continueOnCapturedContext
具有以下含义:如果指定了
true
,这意味着应将延续封送回捕获的原始上下文。如果指定了
false
,则延续可以在任意上下文上运行

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


有关此主题的更多信息,请参阅
ConfigureAwait(bool continueOnCapturedContext)中的参数
continueOnCapturedContext
具有以下含义:如果指定了
true
,这意味着应将延续封送回捕获的原始上下文。如果指定了
false
,则延续可以在任意上下文上运行

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


有关此主题的更多信息,请参阅。

如果您关心保持自定义状态一致,了解调用
ConfigureAwait(false)
的原因会有很大帮助。您似乎感到沮丧,因为
ConfigureAwait(false)
做的正是应该做的。这有点奇怪。这就像问
+
操作符为什么执行加法,以及如何阻止它执行加法。好吧,好吧。如果您不喜欢,则
配置等待(false)
配置
wait
以使其不捕获当前上下文或调度程序,您希望它做什么?您希望它是一个不可操作的吗?您试图用自定义调度程序实现什么?可以用
asynchlocal
来实现吗?您是否在阻止异步代码?我试图理解以及为什么当前自定义任务计划程序在使用
ConfigureAwait(false)调用async后发生更改
。通过您的评论和回答,我现在明白了!我的故事是,我正在开发一个桌面.NET 4.5应用程序,该应用程序使用自定义任务调度程序来限制可以生成的线程数。在尝试将其迁移到.NET Core 3.1时,我不得不用
HttpClient
替换
HttpWebRequest
。起初,我注意到它使用了自定义线程限制器任务调度程序,导致线程不足。这就是为什么我试图确保
HttpClient
异步方法和回调在另一个任务调度程序的同一线程上执行。了解调用
ConfigureAwait(false)的原因会有很大帮助
如果您关心保持自定义状态的一致性,您可能会感到沮丧,因为
配置等待(false)
做的正是应该做的。这有点奇怪。这就像问
+
操作符为什么执行加法,以及如何阻止它执行加法。好吧,好吧。如果您不喜欢,则
配置等待(false)
配置
wait
以使其不捕获当前上下文或调度程序,您希望它做什么?是否希望它成为不可操作?您试图用自定义调度程序实现什么?是否可以用
AsyncLocal
来实现?是否阻止
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>();
    }
}