在 C# 中,线程安全集合和非线程安全集合的核心差异体现在哪些方面?实际开发中应当如何选择和使用这两类集合?
考察说明
考查对 C# 中线程安全集合(如 ConcurrentDictionary、ConcurrentQueue)与非线程安全集合(如 List、Dictionary)在设计目的、性能特征和使用场景上的理解。
回答思路
- 【回答框架 1】线程安全集合(如 System.Collections.Concurrent 命名空间下的类型)通过内部锁或CAS操作保证多线程并发访问时的数据一致性,操作是原子的,无需外部加锁;非线程安全集合(如 List、Dictionary)在并发读写时可能产生数据竞争、状态不一致甚至异常,需要调用方自行加锁。
- 【回答框架 2】使用线程安全集合时,并发性能通常优于给非线程安全集合整体加锁,因为其内部采用细粒度锁或无锁算法,但单个操作的开销可能更高;非线程安全集合在单线程或外部锁保护下性能更好,适合并发度低或读多写少且能保障同步的场景。
- 【回答框架 3】选择原则:若多个线程频繁写入或既有读又有写,且不想手动管理锁,应优先使用线程安全集合;若集合基本固定或仅在初始化后读取,可用非线程安全集合并配合只读访问。常见用法如 ConcurrentDictionary 用于缓存、ConcurrentQueue 用于生产者消费者模式。
- 【回答框架 4】使用时注意:线程安全集合只保证单个操作原子性,多个操作组合(如先检查后插入)仍需要额外同步;非线程安全集合若不得不并发访问,需用 lock 或 Monitor 包裹整个操作区间,并注意锁粒度。
- 【关键点 1】线程安全集合保证单个操作的原子性和并发安全,非线程安全集合不保证,需外部同步
- 【关键点 2】线程安全集合通过细粒度锁或 CAS 提升并发性能,非线程安全集合单线程下性能更优
- 【关键点 3】多线程写或读写混合时优先选线程安全集合,只读场景可用非线程安全集合
- 【关键点 4】组合操作需额外同步,二者均不能自动保证事务性
- 【易错点 1】错误认为线程安全集合所有操作都是线程安全的,如 GetOrAdd 内部的委托仍可能多次执行
- 【易错点 2】忽略组合操作的原子性,导致条件检查与写入之间出现竞态
- 【易错点 3】在性能敏感且并发极低时误用线程安全集合,造成不必要的开销