我正在使用全局变量实现线程间通信。
//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;
}
问题
在上面的代码中,我使用全局var volatile bool is_true
来切换thread_1和thread_2之间的打印。
我想知道在这里使用赋值操作是否是线程安全的?
答案 0 :(得分:57)
此代码不保证在Win32上是线程安全的,因为Win32仅为正确对齐的4字节和指针大小的值保证原子性。 bool
不保证是其中一种类型。 (通常为1字节类型。)
对于那些需要一个如何失败的实际例子的人:
假设bool
是1字节类型。还假设您的is_true
变量恰好存储在另一个bool
变量旁边(让我们称之为other_bool
),这样它们就会共享相同的4字节行。具体来说,假设is_true
位于地址0x1000,other_bool
位于地址0x1001。假设两个值最初都是false
,并且一个线程决定在另一个线程尝试更新is_true
的同时更新other_bool
。可能会发生以下操作序列:
is_true
和true
的4字节值,准备将is_true
设置为other_bool
。线程1读取0x00000000。other_bool
和true
的4字节值,准备将is_true
设置为other_bool
。线程2读取0x00000000。is_true
对应的4字节值中的字节,产生0x00000001。other_bool
对应的4字节值中的字节,产生0x00000100。is_true
现在是true
,other_bool
现在是false
。is_true
现在是false
,other_bool
现在是true
。观察到此序列结束时,is_true
的更新已丢失,因为它被线程2覆盖,线程2捕获旧值is_true
。
碰巧x86非常容忍这种类型的错误,因为它支持字节粒度更新并且具有非常紧凑的内存模型。其他Win32处理器并不宽容。例如,RISC芯片通常不支持字节粒度更新,即使它们存在,它们通常也具有非常弱的内存模型。
答案 1 :(得分:7)
不,它不是.....你需要使用某种类型的锁定原语。根据平台的不同,您可以使用升级版本,也可以使用本机窗口,例如InterlockedCompareExchange。
事实上,在你的情况下,你可能会使用一些线程安全事件机制,这样你就可以“发出信号”你的其他线程开始做你想做的事。
答案 2 :(得分:4)
在所有现代处理器上,您可以假设自然对齐的本机类型的读取和写入是原子的。只要存储器总线至少与读取或写入的类型一样宽,CPU就会在单个总线事务中读取和写入这些类型,这使得其他线程无法在半完成状态下看到它们。在x86和x64上,不能保证读取和写入大于比八个字节都是原子的。这意味着流式SIMD扩展(SSE)寄存器和字符串操作的16字节读写可能不是原子的。
读取和写入非自然对齐的类型 - 例如,编写跨越四字节边界的DWORD - 不保证是原子的。 CPU可能必须作为多个总线事务执行这些读取和写入操作,这可能允许另一个线程在读取或写入过程中修改或查看数据。
答案 3 :(得分:-2)
这段代码的线程安全性不依赖于赋值的原子性。两个线程例程都严格依次工作。没有竞争条件:thread_1将输出内容,直到获得某个随机数,之后它将离开“输出部分”并让其他线程在其中工作。 但有几点值得注意: