Thread-safe, lock-free increment function?
UPDATED: Is there a thread-safe, lock-free and available on all Linux distros increment function availab开发者_如何学Cle in C or C++ ?
2021 answer
C++
12 years have passed since the other answers and it is now in the standard and it is possible to do this since C++11 using the atomic operations library.
#include <iostream>
#include <atomic>
int main() {
std::atomic<int> atomic_i{5};
std::cout << atomic_i.is_lock_free() << std::endl; // 1, which means lock free
atomic_i++;
std::cout << atomic_i << std::endl; // 6
return 0;
}
If interested, this compiles into (with at least -O1 optimisation on gcc):
mov DWORD PTR [rsp-4], 5
lock inc DWORD PTR [rsp-4]
If you want a normal, non-atomic integer that you want to increment in atomic operations only some of the time, you can use atomic_ref
since C++20, if you ensure it's sufficiently aligned. (Some systems under-align some non-atomic types for historical ABI reasons but require natural alignment for atomics to actually work efficiently / correctly. Notably 32-bit x86 with int64_t
and double
where alignof(T) < std::atomic_ref<T>::required_alignment
).
Note that "While any atomic_ref instances referencing an object exists, the object must be exclusively accessed through these atomic_ref instances."
See on Godbolt how it compiles.
// This alignment is redundant for int on most mainstream implementations
// but is important for portability or for wider types like int64_t
alignas(std::atomic_ref<int>::required_alignment) int j = 8;
... // pass the address of j to other threads
{
std::atomic_ref<int> refj(j);
refj++; // atomic operation, j is now 9
}
// refj goes out of scope, so it's ok to use j again
... // some synchronization so we know other threads are done for now
j++; // non-atomic increment, j is now 10
C
You can do the same as in my first example in C11 too, using atomic types.
#include <stdatomic.h>
#include <stdio.h>
int main() {
atomic_int i = 5;
i++; // atomic operation
printf("%d\n", i); // 6
return 0;
}
There isn't a C equivalent for C++20 std::atomic_ref
. If you don't mind using GNU extensions, you can use the builtin functions that C++ atomic
and atomic_ref
are implemented on top of, like GNU C __atomic
builtins: __atomic_fetch_add( &i, 1, __ATOMIC_ACQ_REL)
.
Other compilers for non-Linux systems have their own builtins, for example MSVC with _InterlockedAdd
.
It may happen to work to point an atomic_int *
(aka _Atomic int*
) at the address of an int
, but this is strict-aliasing undefined behaviour.
GLib has functions to do this. You might check out http://library.gnome.org/devel/glib/stable/glib-Atomic-Operations.html
Specifically, it sounds like you want g_atomic_int_inc()
I think these are GNU extensions, and processor specific, but have a look at GNU C Atomic Builtins.
I think there are also atomic "libraries" available that use inline assembly etc. to provide such features.
The current C and C++ standards don't define such a thing. Your implementation may well have one.
精彩评论