面试题 关于线程池 和 ConcurrencyDictionary

今天面试,被问到几个问题,记录如下

1. 你说Task是从线程池中拿线程,那么线程池ThreadPool是谁创建的呢

ThreadPool 是CLR(.NET 运行时)在进程启动时创建并托管的,不是操作系统内核线程池,也不是我们代码手动 new 出来的。 CLR 会维护一组工作线程(Worker Thread) + IO 完成端口线程(IOCP Thread)。Task.Run 默认就是往 CLR ThreadPool 提交任务。线程池线程全部是后台线程。 但是不能无限制创建线程,因为每个线程都有独立栈(默认 1MB),大量原生线程会占用大量内存;CPU 频繁上下文切换,性能暴跌。线程池复用线程,减少上下文切换和内存开销。

2. ConcurrencyDictionary是线程安全的吗?

单个方法调用是线程安全。但是复合操作不是原子操作,这是最核心考点。

// ❌ 不安全,两步操作,中间可被其他线程修改
if (dict.ContainsKey(key))
{
    dict[key] = value;
}

✅ 要用内置原子方法:TryAdd、TryGetValue、AddOrUpdate、GetOrAdd。

底层实现:分段锁(bucket 分段),不是全局锁。相比 Dictionary + lock,并发高的时候锁粒度更小,吞吐更好。

旧版本是细粒度锁;.NET Core/.NET5+ 做了优化,部分路径用 CAS 无锁。

2. GetOrAdd 坑点(高频追问)

dict.GetOrAdd(key, k => CreateExpensiveObject(k));

坑:工厂委托 CreateExpensiveObject 可能被多次执行 **! 就算最终只有一个对象写入字典,高并发下多个线程同时进入,工厂函数会并发跑多次(创建多个实例),只是最终只保留一个。 👉 如果构造对象很耗资源(数据库连接、大对象),工厂里面不能写昂贵操作。 解决:工厂只创建轻量对象,或者外部加锁保证昂贵逻辑只执行一次。

 

  • ConcurrentDictionary 会有锁竞争吗? 答:会,分段锁,同一 bucket 下的 key 依然会锁竞争;不同 bucket 可以并行。
  • 和 Dictionary + lock 对比? 答:低并发:Dictionary+lock 更快;高并发读写:ConcurrentDictionary 吞吐更好。

posted on 2026-10-16 15:51  新西兰程序员  阅读(7)  评论(0)    收藏  举报