最近我看到一些C#项目使用双重检查锁定模式Dictionary.像这样的东西:
private static readonly object _lock = new object();
private static volatile IDictionary<string, object> _cache =
new Dictionary<string, object>();
public static object Create(string key)
{
object val;
if (!_cache.TryGetValue(key, out val))
{
lock (_lock)
{
if (!_cache.TryGetValue(key, out val))
{
val = new object(); // factory construction based on key here.
_cache.Add(key, val);
}
}
}
return val;
}
Run Code Online (Sandbox Code Playgroud)
这段代码是不正确的,因为当(在锁外)迭代Dictionary集合时,可以"增长" 集合.在许多情况下这可能是极不可能的,但仍然是错误的._cache.Add()_cache.TryGetValue
是否有一个简单的程序来证明此代码失败了?
将其纳入单元测试是否有意义?如果是这样,怎么样?
在C++和Double-Checked Locking的Perils中,有一些persudo代码可以正确地实现模式,这是作者建议的.见下文,
Singleton* Singleton::instance () {
Singleton* tmp = pInstance;
... // insert memory barrier (1)
if (tmp == 0) {
Lock lock;
tmp = pInstance;
if (tmp == 0) {
tmp = new Singleton;
... // insert memory barrier (2)
pInstance = tmp;
}
}
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
我只是想知道第一个内存屏障是否可以在return语句的正上方移动?
编辑:另一个问题:在链接文章中,引用vidstige
从技术上讲,您不需要完全双向障碍.第一道屏障必须防止Singleton构造的向下迁移(通过另一个线程); 第二个障碍必须阻止pInstance初始化的向上迁移.这些被称为"获取"和"释放"操作,并且可以产生比硬件(例如Itainum)上的完全障碍更好的性能.
它说第二个障碍不需要是双向的,那么如何防止pInstance的赋值在该障碍之前被移动?即使第一个障碍可以阻止向上迁移,但另一个线程仍然有机会看到未初始化的成员.
编辑:我想我几乎明白第一道屏障的目的.正如sonicoder所指出的,当if返回true时,分支预测可能导致tmp为NULL.为了避免这个问题,必须有一个获取障碍,以防止在读取if之前读取tmp.
第一道屏障与第二道屏障配对以实现同步关系,因此它可以向下移动.
编辑:对于那些对这个问题感兴趣的人,我强烈建议阅读memory-barriers.txt.
我有一段代码可以由多个线程执行,需要执行I/O绑定操作,以初始化存储在一个中的共享资源ConcurrentMap.我需要使这段代码线程安全,并避免不必要的调用来初始化共享资源.这是有缺陷的代码:
private ConcurrentMap<String, Resource> map;
// .....
String key = "somekey";
Resource resource;
if (map.containsKey(key)) {
resource = map.get(key);
} else {
resource = getResource(key); // I/O-bound, expensive operation
map.put(key, resource);
}
Run Code Online (Sandbox Code Playgroud)
使用上面的代码,多个线程可以检查ConcurrentMap并查看资源不存在,并且所有尝试调用getResource()哪个都很昂贵.为了确保共享资源只进行一次初始化,并在资源初始化后使代码有效,我想做这样的事情:
String key = "somekey";
Resource resource;
if (!map.containsKey(key)) {
synchronized (map) {
if (!map.containsKey(key)) {
resource = getResource(key);
map.put(key, resource);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是双重检查锁定的安全版本吗?在我看来,自从调用检查以来ConcurrentMap,它的行为类似于声明的共享资源,volatile从而防止可能发生的任何"部分初始化"问题.
考虑到线程安全的双重检查锁定(对于单例或懒惰初始化),我已经阅读了很多问题.在某些线程中,答案是模式完全被破坏,其他人提出了解决方案.
所以我的问题是:有没有办法在C++中编写一个完全线程安全的双重检查锁定模式?如果是这样,它看起来如何.
我们可以假设C++ 11,如果这让事情变得更容易.据我所知,C++ 11改进了内存模型,可以产生所需的改进.
我知道通过使用双重检查保护变量volatile可以在Java中实现.由于C++ 11从Java中借用了大部分内存模型,所以我认为它有可能,但是如何?
我正在阅读这篇关于"双重检查锁定"的文章,并且在文章的主题之外我想知道为什么在文章的某些方面作者使用下一个成语:
清单7.尝试解决乱序写入问题
Run Code Online (Sandbox Code Playgroud)public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 inst = new Singleton(); //4 } instance = inst; //5 } } } return instance; }
我的问题是:有没有理由用同一个锁同步两次代码?有这个任何目的吗?
提前谢谢了.
C++ 11的新机器模型允许多处理器系统可靠地工作.重组指示.
正如Meyers和Alexandrescu指出的那样,"简单"的双重检查锁定模式实现在C++ 03中并不安全
Singleton* Singleton::instance() {
if (pInstance == 0) { // 1st test
Lock lock;
if (pInstance == 0) { // 2nd test
pInstance = new Singleton;
}
}
return pInstance;
}
Run Code Online (Sandbox Code Playgroud)
他们在文章中表明,无论你作为程序员做什么,在C++ 03中,编译器都有太多的自由:允许以一种你无法确定最终只有一个的方式重新排序指令的例子Singleton.
我现在的问题是:
Lock这里的模拟)时,这个Singleton模式的安全C++ 11实现现在怎么样?为什么模式被认为是破碎的?它看起来很好吗?有任何想法吗?
public static Singleton getInst() {
if (instace == null) createInst();
return instace;
}
private static synchronized createInst() {
if (instace == null) {
instace = new Singleton();
}
}
Run Code Online (Sandbox Code Playgroud) 我有以下代码,可以在同一秒通过多个Web请求调用.因此,我不希望第二个+请求命中数据库,但等到第一个请求命中.
我应该重构这个来使用Lazy<T> 关键字 class吗?如果对Lazy<T>一段代码同时发生10次调用,那么这些调用中有9次会等待第一次调用完成吗?
public class ThemeService : IThemeService
{
private static readonly object SyncLock = new object();
private static IList<Theme> _themes;
private readonly IRepository<Theme> _themeRepository;
<snip snip snip>
#region Implementation of IThemeService
public IList<Theme> Find()
{
if (_themes == null)
{
lock (SyncLock)
{
if (_themes == null)
{
// Load all the themes from the Db.
_themes = _themeRepository.Find().ToList();
}
}
}
return _themes;
}
<sip snip snip>
#endregion
}
Run Code Online (Sandbox Code Playgroud) 我有一个关于双重检查锁定的问题.考虑这个例子:
public class Singleton {
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
Run Code Online (Sandbox Code Playgroud)
据我所知,上面的代码是制作Singleton类的正确方法.
但是,NetBeans要我删除外部if语句,所以它看起来像这样:
public class Singleton {
private static volatile Singleton instance = null;
public static Singleton getInstance() {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
return instance ;
}
}
Run Code Online (Sandbox Code Playgroud)
这两个片段之间的唯一区别在于,在第二个示例中,代码将始终进入同步块,而第一个则不会.为什么我会听NetBeans并删除外部if语句?应该更好地避免锁定.
我发现了这篇文章,但它看起来不对,因为Cell它不能保证set()锁定和锁定之间的同步get().
是否Atomic_.store(true, Ordering::Release)影响其他非原子写操作?
我试着写它AtomicPtr看起来接近Java风格,但它失败了.AtomicPtr在这种情况下,我找不到正确使用的例子.
java ×4
concurrency ×3
.net ×2
c# ×2
c++ ×2
c++11 ×2
singleton ×2
lazy-loading ×1
locking ×1
rust ×1