ThreadPool线程什么时候被释放回ThreadPool?

Eza*_*Eza 1 c# asynchronous async-await

任何人都可以准确地解释(或有资源解释)ThreadPool 线程何时被释放回 ThreadPool?这是一个小示例程序(Dotnet fiddle:https ://dotnetfiddle.net/XRso3q )

    public static async Task Main()
    {
        Console.WriteLine("Start");
        var t1 = ShortWork("SW1");
        var t2 = ShortWork("SW2");
        await Task.Delay(50);
        var t3 = LongWork("LW1");
        Console.WriteLine($"After starting LongWork Thread={Thread.CurrentThread.ManagedThreadId}");
        await Task.WhenAll(t1, t2);
        await t3;
        Console.WriteLine("Done");
    }
    
    public static async Task ShortWork(string name)
    {
        Console.WriteLine($"SHORT Start {name} Thread={Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(500);
        Console.WriteLine($"SHORT End {name} Thread={Thread.CurrentThread.ManagedThreadId}");
    }
    
    public static async Task LongWork(string name)
    {
        Console.WriteLine($"LONG Start {name} Thread={Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(2500);
        Console.WriteLine($"LONG End {name} Thread={Thread.CurrentThread.ManagedThreadId}");
    }
Run Code Online (Sandbox Code Playgroud)

输出:

Start
SHORT Start SW1 Thread=1
SHORT Start SW2 Thread=1
LONG Start LW1 Thread=5
After starting LongWork Thread=5
SHORT End SW1 Thread=7
SHORT End SW2 Thread=5
LONG End LW1 Thread=5
Done
Run Code Online (Sandbox Code Playgroud)

长工作在线程 5 上开始,但在某个时刻,线程 5 被释放回线程池,因为线程 5 能够拾取短 SW1 结束。在 LongWork之后,5 到底什么时候被释放回线程池await Task.Delay(2500)?调用是否将await其释放回线程池?我不认为这种情况就像我在调用 后立即记录线程 id LongWork,该线程仍在线程 5 上运行await Task.WhenAll。 在线程 5 上调用 - 然后将控制释放回任何所谓的“main” - 这是哪里它被释放,因为没有“调用者”可以返回?

我对发生的事情的理解:

  • 在线程 1 上启动,线程 1 执行 ShortWork SW1 和 SW2。
  • Task.Delay(50)正在等待并且线程 1 被释放(因为没有更多的工作要做?)
  • 选择线程 5 在 50 毫秒延迟后继续执行
  • 线程 5 开始LongWork,并且到达等待的2500ms延迟。控制权被释放回主线程,仍在线程 5 上。t1t2等待 - 控制权被释放回主线程(因此线程 5 的工作完成 - 它被释放到线程池)
  • 此时没有线程正在“做”任何事情
  • 当 ShortWork 延迟完成时,将从池中选择线程 5 和 7 来继续每个调用。一旦完成延续,它们就会被释放到池中(?)
  • Task.WhenAll另一个线程获取和 之间的延续await t3,然后立即释放,因为它正在等待t3
  • 选择一个 ThreadPool 线程来执行调用的LongWork延续
  • done最后,ThreadPool 线程在 t3 完成后拾取最后要写入的工作。

另外,为什么 5 会选取 SW1 的末尾,而 7 会选取 LW1 的末尾?这些是刚刚使用的线程。它们是否以某种方式保留为“热门线程并优先考虑出现的延续?”

Ste*_*ary 6

其工作方式await是首先检查其可等待的参数;如果已经完成,则该async方法将同步继续。如果不完整,则该方法返回到其调用者。

理解的第二个关键是所有async方法都像任何其他方法一样在正常调用堆栈上开始同步执行。

这里的第三个有用信息是控制台应用程序需要前台线程才能继续运行,否则它将退出。因此,当您有一个 时async Main,运行时会在后台阻塞返回任务的主线程。因此,在您的示例中,当第一个await被击中时Main,它返回一个任务,并且主线程 1 将剩余的时间花在该任务上。

在此代码中,所有延续均由线程池线程运行。没有指定或保证哪个线程将运行哪个延续。

LONG Start LW1当前的实现使用同步延续,因此在您的示例中,和的线程 idAfter starting LongWork将始终相同。您甚至可以放置一个断点After starting LongWork并查看LongWork延续的调用堆栈中的实际情况Main