是赋值运算符'='原子? [英] is assignment operator '=' atomic?

查看:136
本文介绍了是赋值运算符'='原子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用全局变量实现了线程间通信。

  //全局var 
volatile bool is_true = true;

//线程1
void thread_1()
{
while(1){
int rint = rand()%10;
if(is_true){
cout<< thread_1:<< rint<<< endl; // thread_1打印一些东西
if(rint == 3)
is_true = false; //这里,告诉thread_2开始打印东西
}
}
}

//线程2
void thread_2()
{
while(1){
int rint = rand()%10;
if(!is_true){// if is_true == false
cout< thread_1:<< rint<<< endl; // thread_2打印一些东西
if(rint == 7)// 7
is_true = true; //这里,告诉thread_1开始打印东西
}
}
}

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;
}

问题


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


$

解决方案

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



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


$ b b

假设 bool 是1字节类型。假设您的 is_true 变量恰好存储在另一个 bool 变量附近(我们称之为 other_bool ),以使两者共享相同的4字节行。为了具体性,让我们说 is_true 位于地址0x1000, other_bool 位于地址0x1001。假设两个值最初都是 false ,并且一个线程决定更新 is_true ,同时另一个线程尝试更新 other_bool 。可以进行以下操作序列:




  • 线程1准备将 is_true 设置为 is_true other_bool 的4字节值的 true >。 c> true
  • 通过加载包含 is_true other_bool 的4字节值。
  • 线程1更新与 is_true 对应的4字节值中的字节,产生0x00000001。

  • 线程2更新对应于 other_bool 的4字节值的字节,产生0x00000100。

  • 线程1将更新的值存储到内存。 is_true 现在 true other_bool 现在 false

  • 线程2将更新的值存储到内存。 is_true 现在 false other_bool 现在 true



观察到在这个序列结束时,更新 is_true 丢失,因为它被线程2覆盖,捕获了 is_true 的旧值。



这样就发生了x86是非常宽容这种类型的错误,因为它支持字节细粒更新,并有一个非常紧凑的内存模型。其他Win32处理器不是宽容。例如,RISC芯片通常不支持字节粒度更新,即使它们这样做,它们通常具有非常弱的内存模型。


I'm implementing Inter-Thread Communication using global variable.

//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;
}

Question

In the code above, I use a global var volatile bool is_true to switch printing between thread_1 and thread_2.

I wonder whether it is thread-safe to use assignment operation here?

解决方案

This code is not guaranteed to be thread-safe on Win32, since Win32 guarantees atomicity only for properly-aligned 4-byte and pointer-sized values. bool is not guaranteed to be one of those types. (It is typically a 1-byte type.)

For those who demand an actual example of how this could fail:

Suppose that bool is a 1-byte type. Suppose also that your is_true variable happens to be stored adjacent to another bool variable (let's call it other_bool), so that both of them share the same 4-byte line. For concreteness, let's say that is_true is at address 0x1000 and other_bool is at address 0x1001. Suppose that both values are initially false, and one thread decides to update is_true at the same time another thread tries to update other_bool. The following sequence of operations can occur:

  • Thread 1 prepares to set is_true to true by loading the 4-byte value containing is_true and other_bool. Thread 1 reads 0x00000000.
  • Thread 2 prepares to set other_bool to true by loading the 4-byte value containing is_true and other_bool. Thread 2 reads 0x00000000.
  • Thread 1 updates the byte in the 4-byte value corresponding to is_true, producing 0x00000001.
  • Thread 2 updates the byte in the 4-byte value corresponding to other_bool, producing 0x00000100.
  • Thread 1 stores the updated value to memory. is_true is now true and other_bool is now false.
  • Thread 2 stores the updated value to memory. is_true is now false and other_bool is now true.

Observe that at the end this sequence, the update to is_true was lost, because it was overwritten by thread 2, which captured an old value of is_true.

It so happens that x86 is very forgiving of this type of error because it supports byte-granular updates and has a very tight memory model. Other Win32 processors are not as forgiving. RISC chips, for example, often do not support byte-granular updates, and even if they do, they usually have very weak memory models.

这篇关于是赋值运算符'='原子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆