双重检查 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 值。

  1. When the paper describes insert barriers does it mean just the compiler, CPU or both?

    两个障碍应该是CPU-障碍(这意味着编译器障碍)。

  2. what prevents the compiler from replacing tmp with singleton_object in the code?

    赋值后的障碍

    sobject *tmp = singleton_object;
    

    除其他外意味着(对于CPU和编译器):

    ** 在屏障之前发出的所有 read 访问应该在屏障.

    之前完成

    因此,编译器 不允许读取 singleton_object 变量 而不是 tmp屏障.

  3. 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" 变量 xy 执行屏障。如果没有屏障,读取线程可能会使用 stale 值。

    通常,不同线程之间的每个同步都需要在双方上进行某种"barrier":读取和写入。