是赋值运算符'='原子? [英] is assignment operator '=' atomic?
问题描述
我使用全局变量实现了线程间通信。
//全局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 通过加载包含 - 线程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
和 other_bool
的4字节值。 观察到在这个序列结束时,更新 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
totrue
by loading the 4-byte value containingis_true
andother_bool
. Thread 1 reads 0x00000000. - Thread 2 prepares to set
other_bool
totrue
by loading the 4-byte value containingis_true
andother_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 nowtrue
andother_bool
is nowfalse
. - Thread 2 stores the updated value to memory.
is_true
is nowfalse
andother_bool
is nowtrue
.
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屋!