将代码产生争用条件

本文关键字:争用条件 代码 | 更新日期: 2023-09-27 17:56:32

我有一个文件,其中包含所有资源的基本路径。例如:

build/scripts/script1.js  
build/scripts/script2.js

我当然需要一个基本路径,例如:

https://example.org/SuperDuperSite/build/scripts/script1.js

我希望做的是在启动时使用路径将文件加载到全局字典中。字典只需加载一次。不幸的是,据我所知,基本路径在第一个请求之前不可用。所以在 asp.net 我必须使用application_beginrequest而不是application_start。这样做的不好是现在我必须处理多线程问题。

这迫使我编写以下类型的代码:

  lock(_lock) {
    if (_dictionary == null) {
        LoadDictionary();
    }
  }

当我真正只需要加载一次时,将为每个请求调用它。我当然真的不喜欢这样。出于性能原因,我不想锁定每个请求。一个解决方案,在与我们想出的大学交谈后:

if (_dictionary == null)
{
   lock(_lock) {
      if (_dictionary == null) {
         LoadDictionary();
      }
    }
}

因此,使用此解决方案,我不需要锁定每个请求,但是如果多个线程最终在启动时获得此部分,我将通过检查对象是否在锁内再次为空来保护它。此代码是否有效,还是会遇到争用条件?

将代码产生争用条件

使用双重检查锁时要小心。

是的,它是线程安全的,但是在您的特定代码中,您可能会遇到一个微妙的错误,其中_dictionary已被另一个线程实例化(通过null检查),但尚未完全填充,并且您最终可能会尝试访问部分填充的字典。并最终得到以下两个之一:

  1. 您在阅读字典时缺少结果,或者更糟
  2. 除非您使用的是ConcurrentDictionary(这也会带来性能影响),否则您可能会同时读取和写入字典,并导致死锁(是的,已知Dictionary类会导致许多代码中的死锁)。

bool _dictionaryLoaded标志(仔细检查),在LoadDictionary()末尾翻转为true,可能更好。

或者使用 Lazy<>如果你使用的是 .NET 4。它更干净,您所要做的就是传入LoadDictionary以用作 init 函数。

编辑:Lazy<> 在内部使用双重检查锁实现。