请解释线程安全的懒加载是什么,并说明在 C# 中可以采用哪些实现方式?
考察说明
考察对并发环境下懒加载机制的理解以及 C# 中相关实现方式的掌握。
回答思路
- 【回答框架 1】线程安全的懒加载是指多线程环境下,资源只在首次访问时被创建,且保证创建过程线程安全,避免重复创建或读到未初始化状态。其核心目标是延迟初始化与线程安全性的结合。
- 【回答框架 2】在 C# 中,可以使用 Lazy<T> 类,默认构造函数指定 ExecutionAndPublication 模式,它保证多个线程同时访问时只有一个线程执行初始化,其他线程等待结果。也可通过 LazyThreadSafetyMode 参数显式控制线程安全模式,如 PublicationOnly 允许竞争但最终一致。
- 【回答框架 3】另一种实现是使用双重检查锁(Double-Checked Locking),在属性 getter 中先检查字段是否为空,若为空则进入 lock 块再次检查,然后创建实例。这种方式需要 volatile 字段来确保可见性。
- 【回答框架 4】还可以使用静态构造函数或静态字段初始化,CLR 保证静态构造函数只执行一次,从而天然实现线程安全的懒加载。除此之外,Interlocked.CompareExchange 也可用于实现无锁的懒加载,适合简单场景。
- 【回答框架 5】选择实现方式时需考虑初始化成本、并发性能和代码简洁性。Lazy<T> 是最简洁推荐的方式,双重检查锁适合需要精细控制的场景,静态构造函数适合单例模式。注意避免死锁和性能损耗。
- 【关键点 1】Lazy<T> 默认提供线程安全的懒加载,简化并发初始化。
- 【关键点 2】双重检查锁需配合 volatile 字段保证线程可见性。
- 【关键点 3】静态构造函数由 CLR 保证线程安全且只执行一次。
- 【关键点 4】Interlocked.CompareExchange 可实现无锁懒加载但适用范围有限。
- 【易错点 1】错误地直接使用 lock 但未做双重检查会导致重复初始化。
- 【易错点 2】忘记使用 volatile 可能导致其他线程看到未完全构造的对象。
- 【易错点 3】使用 LazyThreadSafetyMode.None 会失去线程安全保证,需明确场景。