【发布时间】:2013-09-20 21:00:14
【问题描述】:
我在我正在使用的一些代码中看到了这种模式,我明白它的意图是什么,我也明白为什么人们喜欢它,但它有一些不适合我的地方。 模式是 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 不能修改变量。实际上它不会修改它。
【问题讨论】:
-
一般来说,编译器不知道操作系统调用的作用。我认为任何编译器都不会跨操作系统调用移动代码。除非编译器正在对整个程序进行优化,否则编译器通常不知道函数、ctor 或 dtor 包含哪些代码,因此编译器会假设最坏的情况。
标签: c++ multithreading thread-safety mutex compiler-optimization