我++线程安全
这是对这个问题的补充,我可以得出结论,在 c/c++ 中,此类操作不是线程安全的。
我的问题是,就线程安全而言,我们需要在任何情况下获取锁吗?注意这里锁是一个逻辑概念,即使你使用 InterlockedIncrement() 或 c++0x 原子类型,锁在概念上也是通过使用 cmpxchg 来获取的。
例如,如果只有一个写线程和多个读线程,那么读线程会得到奇怪的值吗?我假设
- 类型 i 在 x86 平台上是 32 位,在 x64 平台上是 64 位。
- 旧值或新值都可以。
It is the addition to this question, I can conclude that in c/c++, such operation isn't thread-safe.
my question is need we acquire lock in any case in terms of thread-safety? note here lock is a logical concept, even if you use InterlockedIncrement() or c++0x atomic type, lock is acquired conceptually by using cmpxchg.
For example, if there are only one-write-thread and many-read-threads, can read-thread get strange value? I assume
- The type i is 32-bit on x86 platform or 64-bit on x64 platform.
- Either old value or new value is OK.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对于单个值的单个写入者和多个读取者来说,它是线程安全的,但在一般情况下,此类操作不是线程安全的(因此需要锁或使用原子操作) 。此外,线程安全的含义在这里非常有限。
如果您只是在一个线程中执行
i++
,其他线程将看到旧值或新值。在您提到的两个平台上,这些值是原子存储/加载的,因此它们无法获得一半的值。然而,一般情况下情况并非如此,例如 x86 上的 64 位值将不是原子的,因此读者可以获得旧值的一半和新值的一半。所以这里的线程安全是非常特定于平台的。不过你还是要小心。如果这是一个普通的
int
,优化器可能会简单地丢弃加载操作(也许在寄存器中保留一个副本)。在这种情况下,读者将永远不会获得新的值。如果您要循环执行此操作,这一点至关重要。不幸的是,执行此操作的唯一标准正确方法是使用 C++0x 使用atomic
类型(易失性 现在对于某些编译器来说可以达到此目的)。如果您确实添加了第二个编写器,那么增量运算符当然根本不是线程安全的。但是,您可以在这里使用原子添加函数,这将使其再次成为线程安全的。
In the case of a single writer and many-readers to this single value it is thread-safe, but in the general case such operations are not thread-safe (thus needing a lock or using atomic operations). Also, what thread-safe means is very limited here.
If you simply do
i++
in one thread, the other threads will either see the old value or the new value. On the two platforms you mention the values are atomically stored/loaded, so they cannot get half a value. This is however not true in general, for example a 64-bit value on x86 will not be atomic, so the reader could get half of the old value and half of the new value. So thread-safety here is very platform specific.You still have to be careful however. If this is a plain
int
the optimizer may simply discard the load operation (perhaps keep a copy in a register). In this case the reader will never get a new value. This is vital if you are doing this in a loop. Unfortunately the only standards correct way of doing this is with C++0x using anatomic<T>
type (volatile kind of serves this purpose now for some compilers).If you do add a second writer the increment operator is of course not thread-safe at all. You could however here use an atomic add function which would make it thread safe again.
如果您有权访问 Qt,请查看他们的 QAtomicInt 类。它是完全独立的,可以(我设法做到了)从那里提取所有必要的东西来拥有一个独立的可移植的atomic_int类。
它提供具有屏障语义的原子
fetch_and_store
、fetch_and_add
、compare_and_swap
、increment
和decrement
(获取、释放、完全、无屏障),尽管在 x86 上每个操作都将是完全屏障。还有一个 QAtomicPointer 类模板。
If you have access to Qt, check out their
QAtomicInt
class. It is quite self contained, it is possible (I managed to do it) to pull all the necessary stuff from there to have a standalone portableatomic_int
class.It provides atomic
fetch_and_store
,fetch_and_add
,compare_and_swap
,increment
anddecrement
with barrier semantics (acquire, release, full, no barrier), although on x86 every operation will be full barrier.There is a
QAtomicPointer
class template too.