赋值运算符'='原子?

Alc*_*ott 24 c++ multithreading communication thread-safety visual-c++

我正在使用全局变量实现线程间通信.

//global var
volatile bool is_true = true;

//thread 1
void thread_1()
{
    while(1){
        int rint = rand() % 10;
        if(is_true) {
            cout << "thread_1: "<< rint <<endl;  //thread_1 prints some stuff
            if(rint == 3)
                is_true = false;  //here, tells thread_2 to start printing stuff
        }
    }
}

//thread 2
void thread_2()
{
    while(1){
        int rint = rand() % 10;
        if(! is_true) {  //if is_true == false
            cout << "thread_1: "<< rint <<endl;  //thread_2 prints some stuff
            if(rint == 7)  //7
                is_true = true;  //here, tells thread_1 to start printing stuff
        }
    }
}

int main()
{
    HANDLE t1 = CreateThread(0,0, thread_1, 0,0,0);
    HANDLE t2 = CreateThread(0,0, thread_2, 0,0,0);
    Sleep(9999999);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我使用全局var volatile bool is_true来切换thread_1和thread_2之间的打印.

我想知道在这里使用赋值操作是否是线程安全的

Ray*_*hen 59

此代码不保证在Win32上是线程安全的,因为Win32仅为正确对齐的4字节和指针大小的值保证原子性.bool不保证是这些类型之一.(通常是1字节类型.)

对于那些需要一个如何失败的实际例子的人:

假设这bool是一个1字节的类型.假设您的is_true变量恰好存储在另一个bool变量旁边(让我们调用它other_bool),以便它们共享相同的4字节行.具体来说,假设它is_true位于地址0x1000并且other_bool位于地址0x1001.假设两个值都是最初的false,并且一个线程决定is_true在另一个线程尝试更新的同时更新other_bool.可以发生以下操作序列:

  • 线程1准备设定is_truetrue通过加载包含4字节的值is_trueother_bool.线程1读取0x00000000.
  • 线程2准备设置other_booltrue通过加载包含4字节的值is_trueother_bool.线程2读取0x00000000.
  • 线程1更新4字节值对应的字节is_true,产生0x00000001.
  • 线程2更新对应的4字节值中的字节other_bool,产生0x00000100.
  • 线程1将更新的值存储到存储器中.is_true现在是trueother_bool现在false.
  • 线程2将更新的值存储到存储器中.is_true现在是falseother_bool现在true.

观察到最后这个序列,更新is_true失去了,因为它被线程2覆盖,线程2捕获了旧的值is_true.

碰巧x86非常容忍这种类型的错误,因为它支持字节粒度更新并且具有非常紧凑的内存模型.其他Win32处理器并不宽容.例如,RISC芯片通常不支持字节粒度更新,即使它们存在,它们通常也具有非常弱的内存模型.

  • @KerrekSB当你操作亚原子粒子时,生命是奇怪的. (4认同)
  • 顺便说一下,`volatile`关键字不会强制编译器以线程安全的方式为目标体系结构对齐变量(即将`bool`值存储在32位x86处理器的4字节单元格中)?这将解决问题.有些编译器会这样做吗? (3认同)
  • 这个解释听起来不错,但有点令人担忧:如果我有两个单独的线程使用完全不相关的全局变量,那么根据您的推理,我仍然应该担心这两个线程会相互混淆对方的数据。这听起来不对。 (2认同)
  • @RaymondChen:是的.幸运的是,这不是关于QFT的问题,而是关于C++的问题! (2认同)

Kei*_*las 7

不,它不.....你需要使用某种类型的锁定原语.根据平台的不同,您可以使用升级版本,也可以使用本机窗口,例如InterlockedCompareExchange.

实际上在您的情况下,您可能会使用某些线程安全事件机制,以便您可以"发出信号"您的其他线程以开始执行您想要的操作.

  • 但OP的代码有什么*错误的?你能设计一个破坏的场景吗? (2认同)
  • @Alcott:"OP"表示"原始海报",如提出问题的人.在这种情况下,就是你.:-) (2认同)