【问题标题】:Is use of a Guard class safe from compiler optimization and cpu reordering? [duplicate]使用 Guard 类对编译器优化和 cpu 重新排序是否安全? [复制]
【发布时间】: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


【解决方案1】:

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

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

“编译器屏障”是一个结构,它告诉编译器“你不能从这个点之后移动东西,让它在这个点之前,或者在这个点之前的东西移动到这个点之后”,它的目的正是例如,防止互斥体实现“泄漏”它们旨在保护的简单变量。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 2011-04-18
    • 2017-06-26
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多