c++是int原子的读写吗?

时间:2022-12-31 08:26:07

I have two threads, one updating an int and one reading it. This is a statistic value where the order of the reads and writes is irrelevant.

我有两个线程,一个更新int,一个读取它。这是一个统计值,其中读和写的顺序无关紧要。

My question is, do I need to synchronize access to this multi-byte value anyway? Or, put another way, can part of the write be complete and get interrupted, and then the read happen.

我的问题是,我是否需要同步访问这个多字节值?或者,换句话说,写作的一部分可以完成并被打断,然后读出来。

For example, think of a value = 0x0000FFFF that gets incremented value of 0x00010000.

例如,考虑一个值= 0x0000FFFF,它的递增值为0x00010000。

Is there a time where the value looks like 0x0001FFFF that I should be worried about? Certainly the larger the type, the more possible something like this to happen.

是否有一段时间,值看起来像0x0001FFFF,我应该担心这个?当然,类型越大,这种事情发生的可能性就越大。

I've always synchronized these types of accesses, but was curious what the community thinks.

我总是同步这些类型的访问,但我很好奇社区的想法。

15 个解决方案

#1


39  

At first one might think that reads and writes of the native machine size are atomic but there are a number of issues to deal with including cache coherency between processors/cores. Use atomic operations like Interlocked* on Windows and the equivalent on Linux. C++0x will have an "atomic" template to wrap these in a nice and cross-platform interface. For now if you are using a platform abstraction layer it may provide these functions. ACE does, see the class template ACE_Atomic_Op.

起初,人们可能认为读和写本机大小是原子的,但是有许多问题需要处理,包括处理器/核心之间的缓存一致性。在Windows上使用互锁*之类的原子操作,在Linux上使用类似的操作。c++ 0x将有一个“原子”模板,将它们封装在一个漂亮的跨平台界面中。现在,如果您正在使用平台抽象层,它可能提供这些功能。ACE实现了,请参见类模板ACE_Atomic_Op。

#2


57  

Boy, what a question. The answer to which is:

男孩,一个问题。答案是:

Yes, no, hmmm, well, it depends

是的,不,嗯,这要看情况

It all comes down to the architecture of the system. On an IA32 a correctly aligned address will be an atomic operation. Unaligned writes might be atomic, it depends on the caching system in use. If the memory lies within a single L1 cache line then it is atomic, otherwise it's not. The width of the bus between the CPU and RAM can affect the atomic nature: a correctly aligned 16bit write on an 8086 was atomic whereas the same write on an 8088 wasn't because the 8088 only had an 8 bit bus whereas the 8086 had a 16 bit bus.

这一切都归结于系统的体系结构。在IA32上,一个正确对齐的地址将是一个原子操作。未对齐的写操作可能是原子的,这取决于正在使用的缓存系统。如果内存位于一个L1缓存线内,那么它就是原子的,否则就不是。CPU和RAM之间总线的宽度会影响原子性质:在8086上正确对齐的16位写是原子性的,而在8088上相同的写不是因为8088只有8位总线,而8086只有16位总线。

Also, if you're using C/C++ don't forget to mark the shared value as volatile, otherwise the optimiser will think the variable is never updated in one of your threads.

此外,如果您正在使用C/ c++,请不要忘记将共享值标记为volatile,否则optimiser会认为该变量不会在您的某个线程中更新。

#3


10  

IF you're reading/writing 4-byte value AND it is DWORD-aligned in memory AND you're running on the I32 architecture, THEN reads and writes are atomic.

如果您正在读取/写入4字节的值,并且它在内存中是dword对齐的,并且您正在I32架构上运行,那么读取和写入是原子的。

#4


8  

Yes, you need to synchronize accesses. In C++0x it will be a data race, and undefined behaviour. With POSIX threads it's already undefined behaviour.

是的,您需要同步访问。在c++ 0x中,这将是一个数据竞争,并且是未定义的行为。对于POSIX线程,它已经是未定义的行为。

In practice, you might get bad values if the data type is larger than the native word size. Also, another thread might never see the value written due to optimizations moving the read and/or write.

在实践中,如果数据类型大于本机单词大小,您可能会得到错误的值。另外,另一个线程可能永远不会看到由于移动读和/或写的优化而写入的值。

#5


3  

You must synchronize, but on certain architectures there are efficient ways to do it.

您必须进行同步,但是在某些体系结构中,有一些有效的方法可以做到这一点。

Best is to use subroutines (perhaps masked behind macros) so that you can conditionally replace implementations with platform-specific ones.

最好是使用子例程(可能隐藏在宏后面),这样您就可以有条件地用特定于平台的实现替换实现。

The Linux kernel already has some of this code.

Linux内核已经有一些这样的代码。

#6


3  

On Windows, Interlocked***Exchange***Add is guaranteed to be atomic.

在Windows上,互锁的***Exchange** Add保证是原子的。

#7


1  

To echo what everyone said upstairs, the language pre-C++0x cannot guarantee anything about shared memory access from multiple threads. Any guarantees would be up to the compiler.

为了呼应大家在上面提到的内容,pre- c++ 0x语言不能保证从多个线程访问共享内存。任何保证都取决于编译器。

#8


1  

Definitively NO ! That answer from our highest C++ authority, M. Boost:
Operations on "ordinary" variables are not guaranteed to be atomic.

绝对不!这个答案来自我们最高的c++权威,M. Boost:对于“普通”变量的操作不保证是原子的。

#9


0  

No, they aren't (or at least you can't assume they are). Having said that, there are some tricks to do this atomically, but they typically aren't portable (see Compare-and-swap).

不,它们不是(或者至少你不能认为它们是)。说到这里,有一些技巧可以实现原子化,但它们通常不是可移植的(请参阅比较和交换)。

#10


0  

I agree with many and especially Jason. On windows, one would likely use InterlockedAdd and its friends.

我同意很多人的观点,尤其是杰森。在windows上,人们可能会使用InterlockedAdd及其好友。

#11


0  

Asside from the cache issue mentioned above...

与上面提到的缓存问题相反……

If you port the code to a processor with a smaller register size it will not be atomic anymore.

如果您将代码移植到寄存器大小较小的处理器,那么它将不再是原子的。

IMO, threading issues are too thorny to risk it.

在我看来,线程问题太棘手了,不能冒险。

#12


0  

Lets take this example

让我们用这个例子

int x;
x++;
x=x+5;

The first statement is assumed to be atomic because it translates to a single INC assembly directive that takes a single CPU cycle. However, the second assignment requires several operations so it's clearly not an atomic operation.

第一个语句被认为是原子的,因为它转换为一个单独的CPU周期指令。但是,第二个任务需要几个操作,所以它显然不是一个原子操作。

Another e.g,

另一个如

x=5;

Again, you have to disassemble the code to see what exactly happens here.

同样,您必须对代码进行分解,以查看这里究竟发生了什么。

#13


0  

tc, I think the moment you use a constant ( like 6) , the instruction wouldn't be completed in one machine cycle. Try to see the instruction set of x+=6 as compared to x++

tc,我认为当你使用一个常数(比如6)时,指令不会在一个机器周期内完成。尝试查看x+=6的指令集与x++的指令集

#14


0  

Some people think that ++c is atomic, but have a eye on the assembly generated. For example with 'gcc -S' :

有些人认为++c是原子的,但是关注生成的程序集。例如“gcc -S”:

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

To increment an int, the compiler first load it into a register, and stores it back into the memory. This is not atomic.

为了增加int,编译器首先将它加载到寄存器中,并将其存储回内存中。这不是原子。

#15


-1  

The only portable way is to use the sig_atomic_t type defined in signal.h header for your compiler. In most C and C++ implementations, that is an int. Then declare your variable as "volatile sig_atomic_t."

惟一可移植的方法是使用在signal中定义的sig_atomic_t类型。你的编译器的h头。在大多数C和c++实现中,这是一个int类型,然后将变量声明为“volatile sig_atomic_t”。

#1


39  

At first one might think that reads and writes of the native machine size are atomic but there are a number of issues to deal with including cache coherency between processors/cores. Use atomic operations like Interlocked* on Windows and the equivalent on Linux. C++0x will have an "atomic" template to wrap these in a nice and cross-platform interface. For now if you are using a platform abstraction layer it may provide these functions. ACE does, see the class template ACE_Atomic_Op.

起初,人们可能认为读和写本机大小是原子的,但是有许多问题需要处理,包括处理器/核心之间的缓存一致性。在Windows上使用互锁*之类的原子操作,在Linux上使用类似的操作。c++ 0x将有一个“原子”模板,将它们封装在一个漂亮的跨平台界面中。现在,如果您正在使用平台抽象层,它可能提供这些功能。ACE实现了,请参见类模板ACE_Atomic_Op。

#2


57  

Boy, what a question. The answer to which is:

男孩,一个问题。答案是:

Yes, no, hmmm, well, it depends

是的,不,嗯,这要看情况

It all comes down to the architecture of the system. On an IA32 a correctly aligned address will be an atomic operation. Unaligned writes might be atomic, it depends on the caching system in use. If the memory lies within a single L1 cache line then it is atomic, otherwise it's not. The width of the bus between the CPU and RAM can affect the atomic nature: a correctly aligned 16bit write on an 8086 was atomic whereas the same write on an 8088 wasn't because the 8088 only had an 8 bit bus whereas the 8086 had a 16 bit bus.

这一切都归结于系统的体系结构。在IA32上,一个正确对齐的地址将是一个原子操作。未对齐的写操作可能是原子的,这取决于正在使用的缓存系统。如果内存位于一个L1缓存线内,那么它就是原子的,否则就不是。CPU和RAM之间总线的宽度会影响原子性质:在8086上正确对齐的16位写是原子性的,而在8088上相同的写不是因为8088只有8位总线,而8086只有16位总线。

Also, if you're using C/C++ don't forget to mark the shared value as volatile, otherwise the optimiser will think the variable is never updated in one of your threads.

此外,如果您正在使用C/ c++,请不要忘记将共享值标记为volatile,否则optimiser会认为该变量不会在您的某个线程中更新。

#3


10  

IF you're reading/writing 4-byte value AND it is DWORD-aligned in memory AND you're running on the I32 architecture, THEN reads and writes are atomic.

如果您正在读取/写入4字节的值,并且它在内存中是dword对齐的,并且您正在I32架构上运行,那么读取和写入是原子的。

#4


8  

Yes, you need to synchronize accesses. In C++0x it will be a data race, and undefined behaviour. With POSIX threads it's already undefined behaviour.

是的,您需要同步访问。在c++ 0x中,这将是一个数据竞争,并且是未定义的行为。对于POSIX线程,它已经是未定义的行为。

In practice, you might get bad values if the data type is larger than the native word size. Also, another thread might never see the value written due to optimizations moving the read and/or write.

在实践中,如果数据类型大于本机单词大小,您可能会得到错误的值。另外,另一个线程可能永远不会看到由于移动读和/或写的优化而写入的值。

#5


3  

You must synchronize, but on certain architectures there are efficient ways to do it.

您必须进行同步,但是在某些体系结构中,有一些有效的方法可以做到这一点。

Best is to use subroutines (perhaps masked behind macros) so that you can conditionally replace implementations with platform-specific ones.

最好是使用子例程(可能隐藏在宏后面),这样您就可以有条件地用特定于平台的实现替换实现。

The Linux kernel already has some of this code.

Linux内核已经有一些这样的代码。

#6


3  

On Windows, Interlocked***Exchange***Add is guaranteed to be atomic.

在Windows上,互锁的***Exchange** Add保证是原子的。

#7


1  

To echo what everyone said upstairs, the language pre-C++0x cannot guarantee anything about shared memory access from multiple threads. Any guarantees would be up to the compiler.

为了呼应大家在上面提到的内容,pre- c++ 0x语言不能保证从多个线程访问共享内存。任何保证都取决于编译器。

#8


1  

Definitively NO ! That answer from our highest C++ authority, M. Boost:
Operations on "ordinary" variables are not guaranteed to be atomic.

绝对不!这个答案来自我们最高的c++权威,M. Boost:对于“普通”变量的操作不保证是原子的。

#9


0  

No, they aren't (or at least you can't assume they are). Having said that, there are some tricks to do this atomically, but they typically aren't portable (see Compare-and-swap).

不,它们不是(或者至少你不能认为它们是)。说到这里,有一些技巧可以实现原子化,但它们通常不是可移植的(请参阅比较和交换)。

#10


0  

I agree with many and especially Jason. On windows, one would likely use InterlockedAdd and its friends.

我同意很多人的观点,尤其是杰森。在windows上,人们可能会使用InterlockedAdd及其好友。

#11


0  

Asside from the cache issue mentioned above...

与上面提到的缓存问题相反……

If you port the code to a processor with a smaller register size it will not be atomic anymore.

如果您将代码移植到寄存器大小较小的处理器,那么它将不再是原子的。

IMO, threading issues are too thorny to risk it.

在我看来,线程问题太棘手了,不能冒险。

#12


0  

Lets take this example

让我们用这个例子

int x;
x++;
x=x+5;

The first statement is assumed to be atomic because it translates to a single INC assembly directive that takes a single CPU cycle. However, the second assignment requires several operations so it's clearly not an atomic operation.

第一个语句被认为是原子的,因为它转换为一个单独的CPU周期指令。但是,第二个任务需要几个操作,所以它显然不是一个原子操作。

Another e.g,

另一个如

x=5;

Again, you have to disassemble the code to see what exactly happens here.

同样,您必须对代码进行分解,以查看这里究竟发生了什么。

#13


0  

tc, I think the moment you use a constant ( like 6) , the instruction wouldn't be completed in one machine cycle. Try to see the instruction set of x+=6 as compared to x++

tc,我认为当你使用一个常数(比如6)时,指令不会在一个机器周期内完成。尝试查看x+=6的指令集与x++的指令集

#14


0  

Some people think that ++c is atomic, but have a eye on the assembly generated. For example with 'gcc -S' :

有些人认为++c是原子的,但是关注生成的程序集。例如“gcc -S”:

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

To increment an int, the compiler first load it into a register, and stores it back into the memory. This is not atomic.

为了增加int,编译器首先将它加载到寄存器中,并将其存储回内存中。这不是原子。

#15


-1  

The only portable way is to use the sig_atomic_t type defined in signal.h header for your compiler. In most C and C++ implementations, that is an int. Then declare your variable as "volatile sig_atomic_t."

惟一可移植的方法是使用在signal中定义的sig_atomic_t类型。你的编译器的h头。在大多数C和c++实现中,这是一个int类型,然后将变量声明为“volatile sig_atomic_t”。