原始类型的多线程和运算符++

时间:2011-02-09 21:25:46

标签: c++ multithreading volatile

所以我在SO和其他地方阅读了很多文章,主题是共享变量,多线程和volatile。

如果您考虑以下代码:

class C {
    int x;

public:
    C() : x(0) { }

    void Operation() {
        AcquireMutex();
        ++x;
        ReleaseMutex();
    }
};

现在,如果我已经理解了到目前为止所读到的所有内容,这将是更新x的正确方法,对吧?正确的编译器不会重新排序代码,在调用x之前缓存AcquireMutex()的值,对吗?

我一直习惯用volatile标记这些变量。当恐龙在陆地上漫游时,我在学校里回来的东西,从未真正反映过它。在阅读有关该主题的文章之后,似乎我浪费了几分钟的时间来输入(对于这些类型的用途)无用的关键字......

更新 好的,所以如果我改为Operation()而不是:

void Operation() {
    AcquireMutex();
    ++x;
    ReleaseMutex();
    AcquireMutex();
    ++x;
    ReleaseMutex();
}

现在,让我们忽略互斥体的使用,以及诸如InterlockedIncrement()等内在函数。这是我的观点。

如果x 标记为volatile,上述代码是否可以线程安全?可能是编译器决定在第一个增量之后在寄存器中保存x的最后一个值,然后只是增加寄存器的值,并将其存储在内存中的最后一个增量?如果是这种情况,那么上面的代码不是线程安全的。是什么赋予了?编译器是否会假设在调用任何函数之后,所有缓存的变量都被认为是“脏”的,从而迫使编译器发出读操作?

3 个答案:

答案 0 :(得分:2)

我不太确定马丁是对的。看看这个:

InterlockedIncrement Function

如果32位递增是原子的,为什么需要InterlockedIncremenet

话虽这么说,你永远不应该使用互斥体这种东西,这是一个巨大的浪费。使用CPU内在函数,如win32 api中的Interlocked*函数(以及它们在其他编译器库中的等价函数)。

答案 1 :(得分:2)

volatile对原子性一无所知。其目的是防止缓存不应缓存的内存位置(例如,硬件设备DMA端口)。(编辑:此措辞是由生成的代码引用“缓存”。例如,可以从内存中读取非volatile变量,然后无限期地保存在寄存器中.Arkadiy在下面的注释中提供了更精确的定义。)

正如其他人所指出的那样,C或C ++中的任何操作都不能保证是原子的。您可以根据需要自行管理互斥锁或其他防护。

答案 2 :(得分:1)

如果你在Windows上使用Visual Studio,你可以尝试使用所谓的内在函数:

#include <intrin.h>
class C {
    int x;

public:
    C() : x(0) { }

    void Operation() {
        _InterlockedIncrement(&x);
    }
};

More on compiler intrinsics. 不知道其他操作系统,但我确信也有内在的。

相关问题