解释双重检查锁定中的竞争条件

2023-11-29

void undefined_behaviour_with_double_checked_locking()
{
    if(!resource_ptr)                                    #1
    {
        std::lock_guard<std::mutex> lk(resource_mutex);  #2
        if(!resource_ptr)                                #3
        {
           resource_ptr.reset(new some_resource);        #4
        }
    }
    resource_ptr->do_something();                        #5
}

如果一个线程看到另一个线程写入的指针,它可能不会 查看新创建的 some_resource 实例,导致调用 do_something() 对不正确的值进行操作。这是一个例子 C++ 标准定义为数据竞争的竞争条件类型, 从而指定为未定义的行为。

Question> 我已经看到了上面关于为什么代码存在导致竞争条件的双重检查锁定问题的解释。但是,我仍然很难理解问题所在。也许具体的两线程分步工作流程可以帮助我真正理解上述代码的竞争问题。

书中提到的解决方案之一如下:

std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;

void init_resource()
{
    resource_ptr.reset(new some_resource);
}
void foo()
{
    std::call_once(resource_flag,init_resource); #1
    resource_ptr->do_something();
}
#1 This initialization is called exactly once

有什么意见欢迎留言 -谢谢


在这种情况下(取决于实施.reset and !) 当线程 1 初始化完成时可能会出现问题resource_ptr然后暂停/切换。然后线程 2 出现,执行第一次检查,发现指针不为空,并跳过锁定/完全初始化检查。然后它使用部分初始化的对象(可能会导致不好的事情发生)。然后线程 1 返回并完成初始化,但为时已晚。

部分初始化的原因resource_ptr这是可能的,因为 CPU 被允许重新排序指令(只要它不改变单线程行为)。因此,虽然代码看起来应该完全初始化该对象,然后将其分配给resource_ptr,优化的汇编代码可能会做一些完全不同的事情,并且 CPU 也不能保证按照二进制文件中指定的顺序运行汇编指令!

结论是,当涉及多个线程时,内存栅栏(锁)是保证事情按正确顺序发生的唯一方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解释双重检查锁定中的竞争条件 的相关文章

随机推荐