在 x86 中增量是整数原子
Is increment an integer atomic in x86?
在多核 x86 机器上,假设在 core1 上执行的线程递增一个整数变量a
同时核心 2 上的线程也会递增它。假设a
的初始值为 0,最终会一直2
吗?或者它可能有其他价值?假设a
被声明为 volatile
并且我们没有使用 C++ 的原子变量(例如原子<>和 gcc 中内置的原子操作(。
如果在这种情况下a
的值确实始终为 2,这是否意味着 x86-64 中的long int
也将具有相同的属性,即a
最终将始终为 2?
X86 上的增量内存机器指令仅在与 LOCK 前缀一起使用时才是原子指令。
C 和 C++ 中的 x++ 没有原子行为。如果执行解锁增量,由于处理器正在读取和写入 X 的争用,如果两个单独的处理器尝试增量,则最终可能只看到一个增量或同时看到两个增量(第二个处理器可能已读取初始值,递增它,并在第一个处理器写回其结果后将其写回(。
我相信 C++11 提供原子增量,大多数供应商编译器都有一种惯用的方法来导致某些内置整数类型(通常是 int 和 long(的原子增量;请参阅您的编译器参考手册。
如果要递增"大值"(例如,多精度整数(,则需要使用一些标准锁定机制(如信号量(来实现。
请注意,您还需要担心原子读取。 在 x86 上,如果读取 32 位或 64 位值是 64 位字对齐的,则读取该值恰好是原子值。 对于"大价值"来说,情况并非如此;同样,您需要一些标准锁。
这里有一个证明它在特定实现(gcc(中不是原子的,如您所见(?(,gcc 生成的代码是
- 将值从存储器加载到寄存器
- 递增寄存器的内容
- 将寄存器保存回内存。
这远非原子。
$ cat t.c
volatile int a;
void func(void)
{
a++;
}
[19:51:52 0 ~] $ gcc -O2 -c t.c
[19:51:55 0 ~] $ objdump -d t.o
t.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
0: a1 00 00 00 00 mov 0x0,%eax
5: 83 c0 01 add $0x1,%eax
8: a3 00 00 00 00 mov %eax,0x0
d: c3 ret
不要被mov
指令中的0x0
所迷惑,那里有 4 个字节的空间,链接器将填充生成的内存地址,以便在链接此对象文件时a
那里。
因为没有人回答你的实际问题,而是向你展示如何以一种始终有效的方式做到这一点:
线程 1 加载值 0
线程 2 加载值 0
线程 1 递增存储 1
线程 2 递增其值的本地寄存器副本并存储 1。
如您所见,最终结果是等于 1 而不是 2 的值。 最后不会总是 2
不能保证。您可以使用 lock xadd
指令来实现相同的效果,或者使用 C++ std::atomic
、 或使用 #pragma omp atomic
或为节省重新发明轮子的麻烦而编写的任何其他并发解决方案。
- 如何反转整数参数包
- enum是C++中的宏变量还是整数变量
- 努力将整数转换为链表。不知道我在这里做错了什么
- 整数不会重复超过随机数
- 在C++中手动调整数组大小
- 检查输入是否不是整数或数字
- C++使用整数的压缩数组初始化对象
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- 如何只允许用户输入正整数
- 如何在c++中从文本文件中逐行读取整数
- C++:如何循环通过向量中的整数元素
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 序列化,没有库的整数,得到奇怪的结果
- 在英特尔x86体系结构上使用非AVX指令修改xmm整数寄存器值
- 在 x86 中增量是整数原子
- 正在 x86 计算机上移动超过 32 位的uint64_t整数未定义的行为
- x86上两个128位整数的高效乘法/除法(无64位)
- 为什么使用GCC的x86上的整数溢出会导致无限循环