双重检查 C 中的锁定模式问题
Double check locking pattern issue in C
作为所有研究过这个的人,我已经阅读了这篇论文 http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
我对在 C 结构上实现 DCLP 时遇到的障碍有疑问。这是代码:
typedef struct _singleton_object {
int x;
int y;
} sobject;
static sobject *singleton_object = NULL;
sobject *get_singleton_instance()
{
sobject *tmp = singleton_object;
/* Insert barrier here - compiler or cpu specific or both? */
if (tmp == NULL) {
mutex_lock(&lock); /* assume lock is declared and initialized properly*/
tmp = singleton_object;
if (tmp == NULL) {
tmp = (sobject *)malloc(sizeof(sobject)); /* assume malloc succeeds */
tmp->x = 5;
tmp->y = 7;
/* Insert barrier here - compiler or cpu specific or both ?*/
singleton_object = tmp;
}
mutex_unlock(&lock);
}
return tmp;
}
第一个问题如评论中所述:当论文描述插入障碍时,它仅指编译器,CPU 还是两者兼而有之?我假设两者都是。
我的第二个问题是:是什么阻止编译器在代码中将 tmp 替换为 singleton_object?是什么迫使 singleton_object 加载到 tmp 中,它可能在编译器生成的代码中的寄存器或堆栈中?如果编译器在每次引用 tmp 时实际上从 &singleton_object 加载到寄存器并丢弃该值怎么办?
似乎下面引用的论文中的解决方案取决于我们使用局部变量的事实。如果编译器没有将指针变量中的值加载到局部变量tmp,我们又回到了论文中描述的原始问题。
我的第三个问题是:假设编译器确实将 singleton_object 的值本地复制到寄存器或堆栈(即变量 tmp),为什么我们需要第一个屏障?在函数的开头不应该对 tmp = singleton_object 和 if (tmp == NULL) 进行重新排序,因为与 tmp 存在隐式的先写后读依赖关系。此外,即使我们在第一次加载到 tmp 时从 CPU 的缓存中读取了一个陈旧值,它也应该被读取为 NULL。如果它不是 NULL,则对象构造应该完成,因为构造它的 thread/CPU 应该执行屏障,这确保 x 和 y 的存储对所有 CPU 之前可见singleton_object 具有非 NULL 值。
When the paper describes insert barriers does it mean just the compiler, CPU or both?
两个障碍应该是CPU-障碍(这意味着编译器障碍)。
what prevents the compiler from replacing tmp
with singleton_object
in the code?
赋值后的障碍
sobject *tmp = singleton_object;
除其他外意味着(对于CPU和编译器):
** 在屏障之前发出的所有 read 访问应该在屏障.
之前完成
因此,编译器 不允许读取 singleton_object
变量 而不是 tmp
的 屏障.
If it (singleton_object
) is not NULL, then the object construction should be complete, since the thread/CPU that constructs it should execute the barrier, which ensures that the stores to x
and y
are visible to all CPU's before singleton_object
has a non NULL value.
您需要为 实际使用 这些 "visible" 变量 x
和 y
执行屏障。如果没有屏障,读取线程可能会使用 stale 值。
通常,不同线程之间的每个同步都需要在双方上进行某种"barrier":读取和写入。
作为所有研究过这个的人,我已经阅读了这篇论文 http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
我对在 C 结构上实现 DCLP 时遇到的障碍有疑问。这是代码:
typedef struct _singleton_object {
int x;
int y;
} sobject;
static sobject *singleton_object = NULL;
sobject *get_singleton_instance()
{
sobject *tmp = singleton_object;
/* Insert barrier here - compiler or cpu specific or both? */
if (tmp == NULL) {
mutex_lock(&lock); /* assume lock is declared and initialized properly*/
tmp = singleton_object;
if (tmp == NULL) {
tmp = (sobject *)malloc(sizeof(sobject)); /* assume malloc succeeds */
tmp->x = 5;
tmp->y = 7;
/* Insert barrier here - compiler or cpu specific or both ?*/
singleton_object = tmp;
}
mutex_unlock(&lock);
}
return tmp;
}
第一个问题如评论中所述:当论文描述插入障碍时,它仅指编译器,CPU 还是两者兼而有之?我假设两者都是。
我的第二个问题是:是什么阻止编译器在代码中将 tmp 替换为 singleton_object?是什么迫使 singleton_object 加载到 tmp 中,它可能在编译器生成的代码中的寄存器或堆栈中?如果编译器在每次引用 tmp 时实际上从 &singleton_object 加载到寄存器并丢弃该值怎么办? 似乎下面引用的论文中的解决方案取决于我们使用局部变量的事实。如果编译器没有将指针变量中的值加载到局部变量tmp,我们又回到了论文中描述的原始问题。
我的第三个问题是:假设编译器确实将 singleton_object 的值本地复制到寄存器或堆栈(即变量 tmp),为什么我们需要第一个屏障?在函数的开头不应该对 tmp = singleton_object 和 if (tmp == NULL) 进行重新排序,因为与 tmp 存在隐式的先写后读依赖关系。此外,即使我们在第一次加载到 tmp 时从 CPU 的缓存中读取了一个陈旧值,它也应该被读取为 NULL。如果它不是 NULL,则对象构造应该完成,因为构造它的 thread/CPU 应该执行屏障,这确保 x 和 y 的存储对所有 CPU 之前可见singleton_object 具有非 NULL 值。
When the paper describes insert barriers does it mean just the compiler, CPU or both?
两个障碍应该是CPU-障碍(这意味着编译器障碍)。
what prevents the compiler from replacing
tmp
withsingleton_object
in the code?赋值后的障碍
sobject *tmp = singleton_object;
除其他外意味着(对于CPU和编译器):
** 在屏障之前发出的所有 read 访问应该在屏障.
之前完成因此,编译器 不允许读取
singleton_object
变量 而不是tmp
的 屏障.If it (
singleton_object
) is not NULL, then the object construction should be complete, since the thread/CPU that constructs it should execute the barrier, which ensures that the stores tox
andy
are visible to all CPU's beforesingleton_object
has a non NULL value.您需要为 实际使用 这些 "visible" 变量
x
和y
执行屏障。如果没有屏障,读取线程可能会使用 stale 值。通常,不同线程之间的每个同步都需要在双方上进行某种"barrier":读取和写入。