为何单线程异步控制台应用中ThreadPool大小会增长?
控制台异步代码中线程池线程数增长的原因分析
我参照Stephen Toub的一篇旧博客,尝试让控制台应用仅通过主线程运行异步函数。虽然成功实现了所有异步回调都在主线程执行,但运行后发现ThreadPool的线程数仍出现增长,希望分析背后的原因。
运行环境与结果
我在.NET 6、MacOS环境下测试,Debug和Release模式的输出如下:
Debug模式输出:
$ dotnet ./bin/Debug/net6.0/publish/con.dll ThreadCount: 0 ThreadCount went from 0 to 2 at i=0 ThreadCount went from 2 to 3 at i=91 [1, 1000] Completing on 1 ThreadCount: 3
Release模式输出:
$ dotnet ./bin/Release/net6.0/publish/con.dll ThreadCount: 0 ThreadCount went from 0 to 2 at i=0 ThreadCount went from 2 to 3 at i=401 [1, 1000] Completing on 1 ThreadCount: 3
代码改动说明
注意:我在Program.cs中强制将最后的Complete()调用调度到TaskScheduler.FromCurrentSynchronizationContext(),这与原博客使用TaskScheduler.Default的做法不同。使用默认调度器时,Complete()会在线程池中触发,符合预期。
核心疑问
我想明确两个问题:
- 是什么导致线程池分配新线程?
Task.Delay总会引发这种线程池线程增长的情况吗?
完整测试代码
using System.Collections.Concurrent; internal class Program { private sealed class SingleThreadSynchronizationContext : SynchronizationContext { private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object?>> m_queue = new(); public override void Post(SendOrPostCallback d, object? state) { m_queue.Add(new KeyValuePair<SendOrPostCallback, object?>(d, state)); } public void RunOnCurrentThread() { KeyValuePair<SendOrPostCallback, object?> workItem; while (m_queue.TryTake(out workItem, Timeout.Infinite)) { workItem.Key(workItem.Value); } } public void Complete() { Console.WriteLine($"Completing on {Thread.CurrentThread.ManagedThreadId}"); m_queue.CompleteAdding(); } public static void Run(Func<Task> func) { var syncCtx = new SingleThreadSynchronizationContext(); var prevCtx = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(syncCtx); var t = func(); t.ContinueWith( delegate { syncCtx.Complete(); }, TaskScheduler.FromCurrentSynchronizationContext()); syncCtx.RunOnCurrentThread(); t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } } private static async Task MainAsync(string[] args) { var d = new Dictionary<int, int>(); for (int i = 0; i < 1000; i++) { int id = Thread.CurrentThread.ManagedThreadId; int count; d[id] = d.TryGetValue(id, out count) ? count + 1 : 1; var threadCount = ThreadPool.ThreadCount; await Task.Delay(1); if (ThreadPool.ThreadCount != threadCount) { Console.WriteLine($"ThreadCount went from {threadCount} to {ThreadPool.ThreadCount} at i={i}"); } } foreach (var pair in d) Console.WriteLine(pair); } public static void Main(string[] args) { Console.WriteLine($"ThreadCount: {ThreadPool.ThreadCount}"); SingleThreadSynchronizationContext.Run(() => MainAsync(args)); Console.WriteLine($"ThreadCount: {ThreadPool.ThreadCount}"); } }
内容的提问来源于stack exchange,提问作者Steven Luu
相关产品推荐
相关产品推荐

