是否使用 Guard 类免受编译器优化和 CPU 重新排序的影响

Is use of a Guard class safe from compiler optimization and cpu reordering?

本文关键字:新排序 排序 影响 CPU Guard 优化 编译器 是否      更新时间:2023-10-16

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

我关心的是优化。如果关键部分中的项被标记为易失性,则编译器无法在构造函数的函数调用中移动它们。类似地,如果该项在编译单元之外是可复制的(即,如果一个类具有公共成员,则该成员可以在定义它的单元之外合理地更改)。 编译器不能假定它可以移动该项。但是,如果该项是类的私有项并且未声明易失性,则编译器没有理由认为编译单元之外的任何代码都会影响它,也没有理由相信它必须出现在 Guard 构造函数之前或之后。例如:

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 对变量或函数是什么一无所知。

编辑:我以前读过这个问题编译器在互斥边界周围重新排序?但它并没有完全解决我的疑虑。在该实例中,_field是一个范围未知的变量。在这种情况下,范围是已知的。变量是类的私有成员。在这种情况下,修改它的唯一方法是调用类的公共函数。guard类不可能有指向调用函数的指针或引用,因为我们没有将其传递给构造函数,这意味着编译器可以肯定地知道创建g不能修改变量。事实上,它不会修改它。

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

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

"编译器屏障"是一种结构,它告诉编译器"你不能从这个点之后移动东西,以便它在这个点之前,或者在这个点之前的东西移动到

这个点之后",它的目的正是为了防止互斥体实现在它们要保护的简单变量之间"泄漏"。

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