开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜