我正在使用全局变量实现线程间通信。
//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
在上面的代码中,我使用了全局变量volatile bool is_true
在 thread_1 和 thread_2 之间切换打印。
我想知道这里使用赋值操作是否线程安全?
不保证此代码在 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_true
to true
通过加载包含的 4 字节值is_true
and other_bool
。线程 1 读取 0x00000000。
- 线程2准备设置
other_bool
to true
通过加载包含的 4 字节值is_true
and other_bool
。线程 2 读取 0x00000000。
- 线程1更新4字节值中对应的字节
is_true
,产生 0x00000001。
- 线程2更新4字节值中对应的字节
other_bool
,产生 0x00000100。
- 线程 1 将更新后的值存储到内存中。
is_true
is now true
and other_bool
is now false
.
- 线程 2 将更新后的值存储到内存中。
is_true
is now false
and other_bool
is now true
.
观察到在这个序列的最后,更新为is_true
丢失了,因为它被线程 2 覆盖,线程 2 捕获了旧值is_true
.
碰巧 x86 对此类错误非常宽容,因为它支持字节粒度更新并且具有非常严格的内存模型。其他 Win32 处理器则没有那么宽容。例如,RISC 芯片通常不支持字节粒度更新,即使支持,它们的内存模型通常也非常弱。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)