2

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

  1. The type i is 32-bit on x86 platform or 64-bit on x64 platform.
  2. Either old value or new value is OK.
Community
  • 1
  • 1
Chang
  • 3,953
  • 2
  • 30
  • 43

2 Answers2

4

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 an atomic<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.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
  • Thanks for your reply, so, if I add a second write thread, in this case, could I only acquire lock between write-threads? we also assume that i matchs the machine word-size, i.e. 32-bit on x86 platform or 64-bit on x64 platform. And read-threads can get either old value or new value, other than strange value(i.e. half update), right? – Chang Apr 08 '11 at 06:34
  • volatile doesn't do what you think as has been discussed a million times here. You also failed to mention alignment which is a crucial omission. – David Heffernan Apr 08 '11 at 06:34
  • 1
    @chang that's right but interlocked lock free methods would be better – David Heffernan Apr 08 '11 at 06:35
  • @David, see my question http://stackoverflow.com/questions/5579782/is-the-volatile-the-correct-way-to-inform-the-compiler-about-concurrent-access-to to understand why I say *volatile kind of serves this purpose* (prior to C++0x, C1X it's your only option for atomics) – edA-qa mort-ora-y Apr 08 '11 at 06:42
  • @David, yes the variable has to be properly aligned. Without casting however your variables are normally aligned correctly. – edA-qa mort-ora-y Apr 08 '11 at 06:44
  • @David: note also, "on some compilers". Microsoft gives semantics to `volatile` which are not required by the standard. This is allowed, it just means that code using those extra guarantees isn't portable. – Steve Jessop Apr 08 '11 at 08:49
  • @edA-qa mort-ora-y The response is simply wrong, at least for Posix. If an object (regardless of its type) is accessed by more than one thread, and modified by any of the accessing threads, then all accesses must be synchronized. Anything else results in undefined behavior. – James Kanze Apr 08 '11 at 09:37
  • @James, Posix may not define it, but AMD/Intel certainly have. At the moment the atomicity of data is architecture, compiler, and OS dependent. Only with C++0x,C1x can one start hoping to make such things portable. – edA-qa mort-ora-y Apr 08 '11 at 10:04
0

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 portable atomic_int class.

It provides atomic fetch_and_store, fetch_and_add, compare_and_swap, increment and decrement with barrier semantics (acquire, release, full, no barrier), although on x86 every operation will be full barrier.

There is a QAtomicPointer class template too.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197