两次检查锁定时说明比赛状况 [英] Explain race condition in double checked locking

查看:69
本文介绍了两次检查锁定时说明比赛状况的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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 ++标准定义为数据竞争, 并因此被指定为未定义的行为.

if a thread sees the pointer written by another thread, it might not see the newly-created instance of some_resource, resulting in the call to do_something() operating on incorrect values. This is an example of the type of race condition defined as a data race by the C++ Standard, and thus specified as undefined behaviour.

问题我已经看到了上面的解释,以解释为什么代码具有双重检查锁定问题,从而导致竞争状态.但是,我仍然很难理解问题所在.也许具体的两线程循序渐进的工作流程可以帮助我真正理解上述代码的竞争问题.

Question> I have seen the above explanation for why the code has the double checked locking problem that causes the race condition. However, I still have difficulties to understand what the problem is. Maybe a concrete two-threads step-by-step workflow can help me really understand the race problem for the above the code.

本书提到的一种解决方案如下:

One of the solution mentioned by the book is as follows:

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

欢迎任何评论 -谢谢

推荐答案

最简单的问题场景是some_resource的初始化不依赖resource_ptr的情况.在这种情况下,编译器可以在完全构造some_resource之前自由地为resource_ptr分配一个值.

The simplest problem scenario is in the case where the intialization of some_resource doesn't depend on resource_ptr. In that case, the compiler is free to assign a value to resource_ptr before it fully constructs some_resource.

例如,如果您认为new some_resource的操作包括两个步骤:

For example, if you think of the operation of new some_resource as consisting of two steps:

  • some_resource
  • 分配内存
  • 初始化some_resource(在此讨论中,我将简化假设该初始化不会引发异常)
  • allocate the memory for some_resource
  • initialize some_resource (for this discussion, I'm going to make the simplifying assumption that this initialization can't throw an exception)

然后您会看到编译器可以将代码实现互斥保护的部分实现为:

Then you can see that the compiler could implement the mutex-protected section of code as:

1. allocate memory for `some_resource`
2. store the pointer to the allocated memory in `resource_ptr`
3. initialize `some_resource`

现在很明显,如果另一个线程在第2步和第3步之间执行了该函数,则在未初始化some_resource的情况下可以调用resource_ptr->do_something().

Now it becomes clear that if another thread executes the function between steps 2 and 3, then resource_ptr->do_something() could be called while some_resource has not been initialized.

请注意,在某些处理器体系结构上,也可能在硬件中发生这种重新排序,除非适当的内存屏障(并且此类屏障将由互斥体实现).

Note that it's also possible on some processor architectures for this kind of reordering to occur in hardware unless the proper memory barriers are in place (and such barriers would be implemented by the mutex).

这篇关于两次检查锁定时说明比赛状况的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆