gpt4 book ai didi

c# - .NET 是否在新的不同线程池线程上恢复等待继续,还是重用先前恢复的线程?

转载 作者:行者123 更新时间:2023-12-03 12:44:55 26 4
gpt4 key购买 nike

.NET 是否在新的不同线程池线程上恢复等待继续,还是重用先前恢复的线程?

让我们在下面的 .NET Core 控制台应用程序中的 C# 代码中想象一下:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace NetCoreResume
{
class Program
{
static async Task AsyncThree()
{
await Task.Run(() =>
{
Console.WriteLine($"AsyncThree Task.Run thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}");
});

Console.WriteLine($"AsyncThree continuation thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}");
}

static async Task AsyncTwo()
{
await AsyncThree();

Console.WriteLine($"AsyncTwo continuation thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}");
}

static async Task AsyncOne()
{
await AsyncTwo();

Console.WriteLine($"AsyncOne continuation thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}");
}

static void Main(string[] args)
{
AsyncOne().Wait();

Console.WriteLine("Press any key to end...");
Console.ReadKey();
}
}
}

它会输出:
AsyncThree Task.Run thread id:4
AsyncThree continuation thread id:4
AsyncTwo continuation thread id:4
AsyncOne continuation thread id:4
Press any key to end...

我试图添加 ConfigureAwait(false)每次等待后 Task ,但它会得到相同的结果。

正如我们所见,似乎所有的 await 延续都重用了在 Task.Run 中创建的线程。的 AsyncThree()方法。
我想问一下.NET是否会一直在之前的恢复线程上恢复等待继续,或者在某些情况下它会从线程池中应用一个新的不同线程?

我知道有答案继续将在 上恢复一个线程池线程 在下面的讨论中:

async/await. Where is continuation of awaitable part of method performed?

让我们排除 SynchronizationContext上面链接中的情况,因为我们现在正在讨论 .NET 控制台应用程序。不过我想问一下好像 那个线程池线程在我的例子中总是 thread id 4 ,不知道是不是因为 thread id 4在线程池中总是空闲的,所以每次continuation 都是巧合重用它,还是.NET 有机制会尽可能多地重用之前的recovery 线程?

每个延续是否有可能在不同的线程池线程上恢复,如下所示?
AsyncThree Task.Run thread id:4
AsyncThree continuation thread id:5
AsyncTwo continuation thread id:6
AsyncOne continuation thread id:7
Press any key to end...

最佳答案

Does .NET resume an await continuation on a new different thread pool thread or reuse the thread from a previous resumption?



两者都不。默认情况下,当 await ing Task s, await will capture a "context" and use that to resume the asynchronous method .这个“上下文”是 SynchronizationContext.Current , 除非是 null ,在这种情况下,上下文是 TaskScheduler.Current .在您的示例代码中,上下文是线程池上下文。

谜题的另一部分没有记录: await uses the TaskContinuationOptions.ExecuteSynchronously flag .这意味着当 Task.Run任务完成(由线程 4 ),其继续立即同步运行 - if possible .在您的示例代码中,延续可能会同步运行,因为线程 4 上有足够的堆栈并且继续应该在线程池线程和线程 4 上运行是线程池线程。

同样,当 AsyncThree完成,继续 AsyncTwo立即同步运行 - 再次在线程 4 上运行因为它符合所有标准。

这是一种优化,在像 ASP.NET 这样的场景中特别有用,在这些场景中,通常有一个 async 链。方法并完成一项任务(例如,读取数据库)以完成整个链并发送响应。在这些情况下,您希望避免不必要的线程切换。

一个有趣的副作用是你最终会得到一个“反向调用堆栈”:线程池线程 4运行你的代码然后完成 AsyncThree然后 AsyncTwo然后 AsyncOne ,并且这些完成中的每一个都在实际调用堆栈上。如果在 WriteLine 上放置断点在 AsyncOne (并查看外部代码),您可以看到 ThreadPoolWorkQueue.Dispatch 在哪里(间接)称为 AsyncThree其中(间接)称为 AsyncTwo其中(间接)称为 AsyncOne .

关于c# - .NET 是否在新的不同线程池线程上恢复等待继续,还是重用先前恢复的线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59649998/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com