代码会产生竞争条件吗?

Will code produce a race condition?

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

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();
      }
    }
}

因此,使用此解决方案,我不需要锁定每个请求,但如果多个线程在启动时最终获得此部分,我将通过检查锁内的对象是否再次为 null 来保护它。这段代码会起作用还是我会 运行 进入竞争状态?

使用双重检查锁时要小心

是的,它是线程安全的,但是在您的特定代码中,您可能运行陷入一个微妙的错误,其中_dictionary已经 实例化 由另一个线程(通过 null 检查),但尚未完全 填充 ,您可能最终会尝试访问部分填充的字典.最后得到这两个之一:

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

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

或者如果您使用的是 .NET 4,则使用 Lazy<>。它更简洁,您所要做的就是传入 LoadDictionary 以用作初始化函数。

编辑:懒惰<>是internally implemented with a double-checked lock