std :: lock_guard构造函数实例化顺序

时间:2018-01-30 01:04:07

标签: c++ c++11 locking language-lawyer

我认为这是关于左值实例化排序的一般性问题。

简而言之,这样安全吗?:

void func1()
{
    std::lock_guard< std::mutex > lock( mutex );
    //do some stuff in locked context
}

void func2()
{
    func1();
    std::lock_guard< std::mutex > lock( mutex );
    //do some stuff in locked context
}

我有点担心编译器可能会在从func2中调用func1之前调用lock_guard的构造函数,从而导致死锁。

是否保证这是安全的,或者我需要做这样的事情:

void func1()
{
    std::lock_guard< std::mutex > lock( mutex );
    //do some stuff in locked context
}

void func2()
{
    func1();

    { //lock
        std::lock_guard< std::mutex > lock( mutex );
        //do some stuff in locked context
    } //unlock
}

2 个答案:

答案 0 :(得分:4)

您正在描述的那些内容(函数调用和锁的实例化)在标准中称为完整表达式。

根据C++11 1.9 Program execution /14C++14中的相同位置和文字,C++17 4.6 Program execution /16中的相同文字):

  

在每个值计算和与要评估的下一个完整表达式相关的副作用之前,对与全表达式相关的每个值计算和副作用进行排序。

的情况,看似顺序的操作可以不确定地排序,但这不是其中之一。

顺便说一句,如果您担心单个执行线程可能会尝试重新获取相同的互斥锁两次,那么您可能会发现recursive_mutex派上用场。

除了进一步之外,在你关于C ++ 98和C ++ 03的评论中,线程仅在C ++ 11中引入。在此之前,C ++仍按照C语言使用序列点的概念。

C++98 1.9 Program execution /16C++03 1.9 Program execution /16中,您会找到类似的措辞:

  

每个完整表达式的评估完成时都有一个序列点。

答案 1 :(得分:0)

@ paxdiablo回答的一些补充说明。通常,在特定体系结构上执行生成的程序必须具有与根据C ++标准在源代码中编写的相同的效果。由于优化,允许编译器和CPU 以与源代码匹配的顺序不同的顺序生成和执行指令。

但是,当使用多线程同步构造(例如互斥和原子内存操作)时,不得发生此类重新排序(如果程序员没有明确放宽)。否则,多线程编程根本就不可行。

编译器知道如果它看到这样的代码,它不应该重新排序指令。在CPU级别上,此任务存在内存障碍。

因此,如果锁定互斥锁,则可以确保上面编写的代码已经完成。这是通用的多线程概念,因此我坚信它对C ++也有效。如果我错了,请纠正我。