从编译器优化和cpu重新排序中使用Guard类是否安全?

时间:2013-09-20 21:00:14

标签: c++ multithreading thread-safety mutex compiler-optimization

我已经看到这个模式会出现在我正在使用的一些代码中,并且我明白它的意图是什么,并且我理解为什么人们喜欢它但是有一些关于它的东西并不适合我。 模式是Guard类。你的想法是你有一个关键的部分,你先创建一个Guard g(somemutex)的实例,它获取(或试图获取)somemutex,然后当g超出范围时,它会释放g的析构函数中的互斥锁。 这真的很酷,因为它允许您在函数中的任何位置放入返回,而不必担心记住释放锁。

我关心的是优化。如果临界区中的项标记为volatile,则编译器无法在构造函数的函数调用中移动它们。类似地,如果项目在编译单元外部是可行的(即,如果类具有公共成员,则该成员可以在定义它的单元之外合理地改变。)编译器不能假设它可以移动该项目。但是,如果该项是某个类的私有而没有声明为volatile,则编译器没有理由认为编译单元外的任何代码都会影响它,并且没有理由相信它必须出现在构造函数之前或之后卫兵 例如:

void foo(int i){
    Guard<mutex> g(mymutex);
    notVolatilePrivateVar = i;
}
//gets compiled to look like...
void foo(int i){
    notVolatilePrivateVar = i;
    Guard<mutex> g(mymutex);
}

现在我不是在问它是否可能发生,我在问是否有保证不会发生。

类似于多个问题cpu这样的事情可能会发生,但我认为它更不可能发生,因为cpu根本不了解变量或函数是什么。

编辑:我之前读过这个问题Compiler reordering around mutex boundaries? 但它并没有完全解决我的问题。在那个例子中,_field是一个范围未知的变量。 在这种情况下,范围是已知的。变量是类的私有成员。在这种情况下,修改它的唯一方法是调用类的公共函数。保护类没有办法可能有一个指向调用函数的指针或引用,因为我们没有将它传递给构造函数,这意味着编译器可以肯定地知道创建g不能修改变量。实际上它并没有修改它。

1 个答案:

答案 0 :(得分:0)

由于notVolatilePrivateVar不是函数foo的LOCAL,因此必须将其视为“全局”变量(可能有另一个指针实例与{{1}具有相同的值}可以访问this实体。

换句话说,只要编译器不知道notVolatilePrivateVar的确切实现,或者mutex的实现在实现中有一个“编译器障碍”[它应该具有它是一个内联函数,否则对这种事情不安全]。

“编译器屏障”是一个构造,它告诉编译器“你必须在此之后移动东西,以便它在此之前移动,或者在此点之前移动到此点之后”,其目的正是例如,防止互斥实现“泄漏”它们要保护的简单变量。

换句话说,只要mutex设计正确,编译器就不能“破坏”Guard的承诺。