如何创建线程安全的单例静态实例?(ChaosEngine类场景)
线程安全全局单例ChaosEngine实现方案
要实现全局共享且线程安全的ChaosEngine单例,需要解决两个核心问题:保证单例实例唯一且线程安全地初始化,保证实例内部状态的访问和修改是线程同步的。以下是两种可行的实现方案:
方案一:双重检查锁定(Double-Check Locking)
通过双重检查锁定保证单例仅初始化一次,同时使用内部锁保护所有状态操作:
public class ChaosEngine { // 内部状态字段 private int _Seed; private int _INext; private int _INextP; private int[] _SeedArray; // 单例初始化锁(保证实例仅创建一次) private static readonly object _singletonLock = new object(); private static ChaosEngine _shared; // 内部状态锁(保护所有状态的读写操作) private readonly object _stateLock = new object(); public int Seed { get { lock(_stateLock) return _Seed; } set { lock(_stateLock) Reseed(value); } } // 私有构造函数,禁止外部实例化 private ChaosEngine() { Reseed(Environment.TickCount); } public static ChaosEngine Shared { get { // 第一次检查,避免不必要的锁 if (_shared == null) { lock(_singletonLock) { // 第二次检查,保证仅初始化一次 if (_shared == null) { _shared = new ChaosEngine(); } } } return _shared; } } private void Reseed(int seed) { lock(_stateLock) { _Seed = seed; // 以下是原ChaosEngine的SeedArray初始化逻辑(参考Random类实现) _SeedArray = new int[56]; int num = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed); int num2 = 161803398 - num; _SeedArray[55] = num2; int num3 = 1; for (int i = 1; i < 55; i++) { int num4 = 21 * i % 55; _SeedArray[num4] = num3; num3 = num2 - num3; if (num3 < 0) num3 += int.MaxValue; num2 = _SeedArray[num4]; } for (int j = 1; j < 5; j++) { for (int k = 1; k < 56; k++) { _SeedArray[k] -= _SeedArray[1 + (k + 30) % 55]; if (_SeedArray[k] < 0) _SeedArray[k] += int.MaxValue; } } _INext = 0; _INextP = 21; } } // 示例随机数生成方法,必须加锁保证状态同步 public int Next() { lock(_stateLock) { if (++_INext >= 56) _INext = 1; if (++_INextP >= 56) _INextP = 1; int num = _SeedArray[_INext] - _SeedArray[_INextP]; if (num < 0) num += int.MaxValue; _SeedArray[_INext] = num; return num; } } }
方案二:使用Lazy(推荐)
.NET的Lazy<T>类型默认支持线程安全的延迟初始化,无需手动写双重检查锁,代码更简洁可靠:
public class ChaosEngine { // 内部状态字段 private int _Seed; private int _INext; private int _INextP; private int[] _SeedArray; // 线程安全的延迟初始化单例 private static readonly Lazy<ChaosEngine> _lazyShared = new Lazy<ChaosEngine>( () => new ChaosEngine(), LazyThreadSafetyMode.ExecutionAndPublication ); // 内部状态锁 private readonly object _stateLock = new object(); public int Seed { get { lock(_stateLock) return _Seed; } set { lock(_stateLock) Reseed(value); } } private ChaosEngine() { Reseed(Environment.TickCount); } public static ChaosEngine Shared => _lazyShared.Value; private void Reseed(int seed) { lock(_stateLock) { _Seed = seed; // 原SeedArray初始化逻辑 _SeedArray = new int[56]; int num = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed); int num2 = 161803398 - num; _SeedArray[55] = num2; int num3 = 1; for (int i = 1; i < 55; i++) { int num4 = 21 * i % 55; _SeedArray[num4] = num3; num3 = num2 - num3; if (num3 < 0) num3 += int.MaxValue; num2 = _SeedArray[num4]; } for (int j = 1; j < 5; j++) { for (int k = 1; k < 56; k++) { _SeedArray[k] -= _SeedArray[1 + (k + 30) % 55]; if (_SeedArray[k] < 0) _SeedArray[k] += int.MaxValue; } } _INext = 0; _INextP = 21; } } public int Next() { lock(_stateLock) { if (++_INext >= 56) _INext = 1; if (++_INextP >= 56) _INextP = 1; int num = _SeedArray[_INext] - _SeedArray[_INextP]; if (num < 0) num += int.MaxValue; _SeedArray[_INext] = num; return num; } } }
关键注意事项
- 私有构造函数:必须将构造函数设为私有,防止外部代码创建新的ChaosEngine实例,保证单例唯一性。
- 内部状态锁:所有访问或修改
_Seed、_INext、_INextP、_SeedArray的操作必须在lock(_stateLock)块内执行,避免多线程竞态条件导致状态不一致。 - 锁对象选择:使用私有
readonly object作为锁对象,不要用lock(this)或lock(typeof(ChaosEngine)),前者可能被外部代码锁定引发死锁,后者是全局类型锁,易受其他代码干扰。
内容的提问来源于stack exchange,提问作者user3915050
相关产品推荐
相关产品推荐

