更新:在 C 或 C++ 中可用的所有 Linux 发行版增量功能上是否有线程安全、无锁和可用?
4 回答
2021年答案
C++
自其他答案以来已经过去了 12 年,现在它已成为标准,并且可以使用原子操作库从 C++11 开始执行此操作。
#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;
}
如果有兴趣,这将编译为(在 gcc 上至少使用 -O1 优化):
mov DWORD PTR [rsp-4], 5
lock inc DWORD PTR [rsp-4]
如果您想要一个普通的非原子整数,并且只想在某些时候在原子操作中递增,那么您可以atomic_ref
从 C++20 开始使用,前提是确保它充分对齐。(由于历史 ABI 原因,某些系统未对齐某些非原子类型,但需要自然对齐原子才能实际有效/正确地工作。尤其是 32 位 x86int64_t
和double
where alignof(T) < std::atomic_ref<T>::required_alignment
)。
请注意,“虽然存在引用对象的任何 atomic_ref 实例,但必须通过这些 atomic_ref 实例独占访问该对象。” 在 Godbolt 上
查看它是如何编译的。
// 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
您也可以使用atomic types做与我在 C11 中的第一个示例中相同的操作。
#include <stdatomic.h>
#include <stdio.h>
int main() {
atomic_int i = 5;
i++; // atomic operation
printf("%d\n", i); // 6
return 0;
}
C++20 没有 C 等效项std::atomic_ref
。如果您不介意使用 GNU 扩展,您可以使用 C++ 的内置函数atomic
并atomic_ref
在其上实现,例如GNU C__atomic
内置函数:__atomic_fetch_add( &i, 1, __ATOMIC_ACQ_REL)
.
用于非 Linux 系统的其他编译器有自己的内置函数,例如MSVC和_InterlockedAdd
.
atomic_int *
将 an (aka )指向 an_Atomic int*
的地址可能会起作用int
,但这是严格混叠未定义的行为。
我认为这些是 GNU 扩展,并且是特定于处理器的,但请查看GNU C Atomic Builtins。
我认为还有可用的原子“库”使用内联汇编等来提供此类功能。
GLib 具有执行此操作的功能。你可以看看http://library.gnome.org/devel/glib/stable/glib-Atomic-Operations.html
具体来说,听起来你想要 g_atomic_int_inc()
当前的 C 和 C++ 标准没有定义这样的东西。您的实现很可能有一个。