C++ 是赋值运算符'=';原子的?
我正在使用全局变量实现线程间通信C++ 是赋值运算符'=';原子的?,c++,multithreading,thread-safety,communication,visual-c++,C++,Multithreading,Thread Safety,Communication,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 st
//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;
}
//全局变量
volatile bool为真=真;
//线程1
无效线程_1()
{
而(1){
int rint=rand()%10;
如果(是真的){
不,不是……你需要使用某种类型的锁定原语。根据平台的不同,你可以使用boost原语,如果使用本机windows,也可以使用InterlocatedCompareeExchange之类的原语
事实上,在您的情况下,您可能需要使用一些线程安全事件机制,以便您可以“通知”其他线程开始执行您想要的操作。这段代码的线程安全性不取决于分配的原子性。两个线程例程严格轮流工作。没有竞争条件:线程1将输出填充直到得到某个随机数,之后它将离开“输出部分”,让另一个线程在其中工作。
但有几件事值得注意:
- rand()函数可能不是线程安全的(但不是这里给出的代码中的问题)
- 不应使用Win32函数CreateThread(),尤其是在使用CRT库函数(可能)使用全局变量时。请改用_beginthreadex()
由于Win32仅为正确对齐的4字节和指针大小的值保证原子性,因此不能保证此代码在Win32上是线程安全的。bool
不保证为这些类型之一。(通常为1字节类型。)
对于那些要求提供如何失败的实际例子的人:
假设bool
是一个1字节类型。还假设您的为true
变量恰好存储在另一个bool
变量旁边(我们称之为其他bool
),使它们共享相同的4字节行。具体来说,让我们假设is_true
位于地址0x1000,而其他
位于地址0x1001。假设两个值最初都是false
,一个线程决定更新is_true
,同时另一个线程尝试更新其他线程_bool
。可能会发生以下操作序列:
- 线程1通过加载包含
is\u true
和other\u bool
的4字节值,准备将is\u true
设置为true
。线程1读取0x00000000
- 线程2通过加载包含
is\u-true
和other\u-bool
的4字节值,准备将other\u-bool
设置为true
。线程2读取0x00000000
- 线程1更新与
为true
对应的4字节值中的字节,生成0x00000001
- 线程2更新与
other\bool
对应的4字节值中的字节,生成0x00000100
- 线程1将更新后的值存储到内存中。
为true
现在为true
,其他布尔
现在为false
- 线程2将更新后的值存储到内存中。
为true
现在为false
,其他线程现在为true
请注意,在该序列结束时,is\u true
的更新丢失,因为它被线程2覆盖,线程2捕获了一个旧值is\u true
碰巧的是,x86非常宽容这种类型的错误,因为它支持字节粒度更新,并且具有非常紧凑的内存模型。其他Win32处理器没有那么宽容。例如,RISC芯片通常不支持字节粒度更新,即使支持,它们通常也具有非常弱的内存模型。在所有现代处理器上rs,您可以假设自然对齐的本机类型的读写是原子的。只要内存总线的宽度至少与正在读写的类型相同,CPU就会在单个总线事务中读写这些类型,使得其他线程无法看到它们处于半完成状态。在x86和x64上,这是无法保证的大于8字节的读写是原子的。这意味着16字节的数据流单指令多数据扩展指令集(SSE)寄存器读写和字符串操作可能不是原子的
例如,不自然对齐的类型的读写,写跨越四字节边界的dWord不能保证是原子的。CPU可能需要将这些读写作为多个总线事务来处理,这可以允许另一个线程在读取或写入中间修改或查看数据。一个原子交换原语,但我不能设计出一个你会遇到问题的场景…@KerrekSB,这个场景?好吧,我只是临时设计了它来演示我的问题:)好吧,我的意思是一系列的加载和存储,这些加载和存储将被充分破坏,使两个线程都进入关键部分…通常一个线程应该能够演示这是一个序列来说明为什么某些代码是不正确的。虽然我在这里看不到。我仍然不喜欢这些代码,但我无法证明原因。我认为,可以使用不同的标志,而不仅仅是一个。第一个标志是从thread1向thread2发送信号,第二个标志是从thread2向thread1发送信号。然后,您将有两个变量,每个变量有一个writer和man你不能使用volatile
进行线程间通信。(好吧,在某些非常特殊的情况下,你可以这样做-这里没有。)不过OP的代码有什么问题吗?你能设计出一个发生故障的场景吗?@KerrekSB,对不起,OP?那是什么?@Alcott:“OP”的意思是“原始海报”,如中所示,提出问题的人。在本例中,是您。:-)锁定原语用于两个或多个线程之间存在竞争的情况。在这里给出的代码中没有这种情况。线程1在关键部分工作,直到它滚动某个随机数。然后它更改全局变量并离开。只有在另一个之后苏氨酸