多线程环境下如何让指定代码仅执行一次?现有锁方案失效求助
问题原因分析
- 你的锁对象
locker和状态标识IsModelLoaded是实例级成员:如果每个线程都创建了独立的类实例,那么每个实例都会持有独立的锁和状态标识,锁完全不会生效,自然每个线程都会执行初始化逻辑,这是你遇到的最核心问题。 - 你尝试的
[MethodImpl(MethodImplOptions.Synchronized)]如果修饰实例方法,默认锁的是当前实例,多实例场景下同样会失效,且该特性锁粒度不可控,本身就不推荐使用。 - Release模式下失效的核心原因是你没有保证状态标识的跨线程可见性,或是多实例场景下状态完全隔离,Debug模式下的调试附加逻辑会隐形添加内存屏障,所以会碰巧生效。
修复方案
方案1:静态锁+双重检查锁定(适合全局仅一次初始化的场景)
把锁对象和状态标识改为静态成员,保证所有类实例共享同一个锁和状态,同时增加外层无锁判断提升性能:
// 静态成员,全局所有实例共享 private static readonly object _locker = new object(); private static bool _isInitialized = false; public void Initialize() { // 外层无锁判断,避免每次调用都抢锁 if (_isInitialized) return; lock (_locker) { // 抢到锁后二次判断,避免多线程同时进入外层判断后重复执行 if (_isInitialized) return; Console.WriteLine("PrintMsg"); // 环境搭建、文件夹创建等初始化逻辑写在此处 _isInitialized = true; } }
该方案完全兼容Release模式,不会出现多线程重复执行的问题。
方案2:使用.NET内置Lazy<T>(优先推荐,无手动锁出错风险)
.NET Framework 4.0+、.NET Core/.NET 5+都原生提供了Lazy<T>类,专门用于线程安全的延迟初始化,默认保证初始化逻辑仅执行一次,不需要手动处理锁和状态判断:
// 静态Lazy对象,初始化逻辑写在委托内 private static readonly Lazy<bool> _initializer = new Lazy<bool>(() => { Console.WriteLine("PrintMsg"); // 环境搭建、文件夹创建等初始化逻辑写在此处 return true; }, isThreadSafe: true); public void Initialize() { // 只需访问Value属性,初始化逻辑只会在第一次访问时执行一次 var _ = _initializer.Value; }
该方案底层已经处理了所有线程安全和内存可见性问题,代码更简洁,不会出现手动锁的常见错误。
内容的提问来源于stack exchange,提问作者Naseem
相关产品推荐
相关产品推荐

