将代码产生争用条件
本文关键字:争用条件 代码 | 更新日期: 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
检查),但尚未完全填充,并且您最终可能会尝试访问部分填充的字典。并最终得到以下两个之一:
- 您在阅读字典时缺少结果,或者更糟
- 除非您使用的是
ConcurrentDictionary
(这也会带来性能影响),否则您可能会同时读取和写入字典,并导致死锁(是的,已知Dictionary
类会导致许多代码中的死锁)。
bool _dictionaryLoaded
标志(仔细检查),在LoadDictionary()
末尾翻转为true
,可能更好。
或者使用 Lazy<>如果你使用的是 .NET 4。它更干净,您所要做的就是传入LoadDictionary
以用作 init 函数。
编辑:Lazy<> 在内部使用双重检查锁实现。