is assignment operator #39;=#39; atomic?(赋值运算符“=是原子的吗?)
问题描述
我正在使用全局变量实现线程间通信.
//全局变量volatile bool is_true = true;//线程1无效线程_1(){而(1){int rint = rand() % 10;如果(is_true){cout<<线程_1:"<<rint<<endl;//thread_1 打印一些东西如果(rint == 3)is_true = false;//这里,告诉thread_2开始打印东西}}}//线程2void thread_2(){而(1){int rint = rand() % 10;if(!is_true) {//if is_true == falsecout<<线程_1:"<<rint<<endl;//thread_2 打印一些东西if(rint == 7)//7is_true = 真;//这里,告诉thread_1开始打印东西}}}int main(){处理 t1 = CreateThread(0,0, thread_1, 0,0,0);处理 t2 = CreateThread(0,0, thread_2, 0,0,0);睡眠(9999999);返回0;}
问题
在上面的代码中,我使用全局变量 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
和other_bool
的 4 字节值将is_true
设置为true
.线程 1 读取 0x00000000. - 线程 2 准备通过加载包含
is_true
和other_bool
的 4 字节值将other_bool
设置为true
.线程 2 读取 0x00000000. - 线程 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
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.
这篇关于赋值运算符“="是原子的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:赋值运算符“="是原子的吗?
基础教程推荐
- 在 C++ 中循环遍历所有 Lua 全局变量 2021-01-01
- 使用从字符串中提取的参数调用函数 2022-01-01
- 如何在不破坏 vtbl 的情况下做相当于 memset(this, ...) 的操作? 2022-01-01
- 管理共享内存应该分配多少内存?(助推) 2022-12-07
- 如何“在 Finder 中显示"或“在资源管理器中显 2021-01-01
- 从 std::cin 读取密码 2021-01-01
- 如何使图像调整大小以在 Qt 中缩放? 2021-01-01
- 为什么语句不能出现在命名空间范围内? 2021-01-01
- Windows Media Foundation 录制音频 2021-01-01
- 为 C/C++ 中的项目的 makefile 生成依赖项 2022-01-01