在新标准 C++ 原子递增操作中,在递增值之前检查先决条件是否表明原子值小于指定值?
我可以比下面的代码更容易、更快吗?
int atomic_inc(std::atomic_int& val, int less_than) {
int new_val;
int old_val = val.load();
do
{
if (old_val > less_than) return old_val;
new_val = old_val + 1;
} while (!val.compare_exchange_weak(old_val, new_val));
return new_val;
}
Run Code Online (Sandbox Code Playgroud)
如果有人不知道compare_exchange_weak是如何工作的:compare_exchange_weak读取val,与old_val进行比较,如果不相等则将val保存到old_val。如果相等则将new_val保存到val。
我有一个简单的布尔值,我需要以线程安全的方式进行测试和设置.如果一个线程已经在工作,我希望第二个线程退出.如果我理解std::atomic_flag正确,这应该工作正常.但是,我不自信我理解std::atomic_flag正确:)我似乎无法在网上找到很多简单的例子,除了这个螺旋锁示例:
// myclass.cpp
#using <atomic>
namespace // anonymous namespace
{
std::atomic_flag _my_flag = ATOMIC_FLAG_INIT;
} // ns
myclass::do_something()
{
if ( !::_my_flag.test_and_set() ) )
{
// do my stuff here; handle errors and clear flag when done
try
{
// do my stuff here
}
catch ( ... )
{
// handle exception
}
::_my_flag.clear(); // clear my flag, we're done doing stuff
}
// else, we're already doing something in another thread, let's exit
} …Run Code Online (Sandbox Code Playgroud) 我有一个类型class A的成员变量._atomicVarstd::atomic<int>
#include <atomic>
class A
{
public:
A();
~A();
private:
std::atomic<int> _atomicVar;
};
Run Code Online (Sandbox Code Playgroud)
如果我构建项目,我会收到以下错误:
error C2280: 'std::atomic<int>::atomic(const std::atomic<int> &)' : attempting to reference a deleted function
Run Code Online (Sandbox Code Playgroud)
我主要是一名C#开发人员,所以我还不知道C++的每一个细节.我不知道我在哪里使用复制文件atomic<int>.
我也尝试初始化_atomicVar:
std::atomic<int> _atomicVar { 0 };
Run Code Online (Sandbox Code Playgroud)
......但那没用.
我希望_atomicVar(没有显式初始化)将使用默认值初始化int.
你能告诉我为什么会出现这个错误吗?
我正在C中实现一个需要使用多个线程的引用计数系统.因此,我需要一种方法来减少整数引用计数,并测试一次原子操作结果是否为零.我可以使用C11 stdatomic.h,但似乎没有减量和测试操作.
这是最好的(即最便携的)方式吗?我可以使用这些stdatomic.h功能来实现吗?
这是引用计数(伪代码)的核心:
retain(object) {
++object.ref_count; // pretty easy to make this atomic
}
release(object) {
if (--object.ref_count == 0) // need this to be atomic also
free(object)
}
Run Code Online (Sandbox Code Playgroud) 假设我有2个线程:
int value = 0;
std::atomic<bool> ready = false;
thread 1:
value = 1
ready = true;
thread 2:
while (!ready);
std::cout << value;
Run Code Online (Sandbox Code Playgroud)
这个程序能输出0吗?
我读到了有关C ++内存模型的信息-具体来说,是顺序一致性,我认为这是默认设置,但并不是特别清楚。是仅要求编译器相对于彼此以正确的顺序放置原子操作,还是相对于所有其他操作以正确的顺序放置原子操作?
将 astd::atomic_flag与 an std::atomic_bool(又名std::atomic<bool>)进行比较,在我看来, astd::atomic_flag只是具有更简单的界面。它仅提供测试+设置和清除标志,同时std::atomic_bool还为多个运算符提供重载。
我的一个问题是关于术语的:“加载或存储操作”是什么意思?这是否意味着不能任意读取和修改 astd::atomic_flag的值?
此外,我想知道,将 astd::atomic_bool用于自旋锁时会更快吗?在我看来,std::atomic_flag在自旋锁期间总是必须读取和写入:
while (my_atomic_flag.test_and_set()); // spin-lock
Run Code Online (Sandbox Code Playgroud)
而 anstd::atomic_bool只需执行读取操作(假设原子 bool 是无锁实现的):
while (my_atomic_bool); // spin-lock
Run Code Online (Sandbox Code Playgroud)
严格来说,a 是否std::atomic_flag比 a 更有效率std::atomic_bool,或者反过来也可能吗?自旋锁应该使用什么?
根据https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html,存在:
type __atomic_load_n (type *ptr, int memorder)
Run Code Online (Sandbox Code Playgroud)
和(“通用”):
void __atomic_load (type *ptr, type *ret, int memorder)
Run Code Online (Sandbox Code Playgroud)
然后
void __atomic_store_n (type *ptr, type val, int memorder)
Run Code Online (Sandbox Code Playgroud)
和(“通用”)
void __atomic_store (type *ptr, type *val, int memorder)
Run Code Online (Sandbox Code Playgroud)
等等
后一个版本有什么通用性(前一个版本不通用),为什么需要它们?
如果T是 C++ 基本类型,并且如果std::atomic<T>::is_lock_free()返回true,那么是否有任何东西std::atomic<T>是无等待的(不仅仅是无锁的)?像, load, store, fetch_add, fetch_sub, compare_exchange_weak, 和compare_exchange_strong。
您是否还可以根据 C++ 标准中指定的内容以及 Clang 和/或 GCC(您选择的版本)中实现的内容进行回答。
我最喜欢的无锁和无等待定义来自C++ Concurrency in Action(免费提供)。如果满足下面的第一个条件,则算法是无锁的,如果满足以下两个条件,则是无等待的:
如果我有atomic_bool flag;,我如何编写 C 代码来切换它,使其原子、可移植且高效?关于“高效”,我希望它能够在 x86_64 上组装到lock xorb $1, flag(%rip). “显而易见”的东西flag = !flag;已经被淘汰了,因为它实际上不是原子的。我的下一个猜测是flag ^= true;,它在 GCC 上组装成一团糟:
movzbl flag(%rip), %eax
0:
movb %al, -1(%rsp)
xorl $1, %eax
movl %eax, %edx
movzbl -1(%rsp), %eax
lock cmpxchgb %dl, flag(%rip)
jne 0b
Run Code Online (Sandbox Code Playgroud)
Clang 上的混乱:
movb flag(%rip), %al
0:
andb $1, %al
movl %eax, %ecx
xorb $1, %cl
lock cmpxchgb %cl, flag(%rip)
jne 0b
Run Code Online (Sandbox Code Playgroud)
然后我尝试通过这样做来指定较弱的内存顺序atomic_fetch_xor_explicit(&flag, true, memory_order_acq_rel);。这实现了我在 Clang 上想要的功能,但是 GCC 现在完全无法使用error: operand type '_Atomic …
在 C++20 中,我们能够在原子变量上休眠,等待它们的值改变。我们通过使用std::atomic::wait方法来做到这一点。
不幸的是,虽然wait已经标准化,wait_for而wait_until不是。这意味着我们不能在超时的原子变量上睡觉。
无论如何,使用 Windows上的WaitOnAddress和Linux 上的futex系统调用在幕后实现原子变量睡眠。
解决上述问题(无法在具有超时的原子变量上休眠),我可以在 Windows 上将内存地址传递std::atomic给 toWaitOnAddress并且它将(有点)在没有 UB 的情况下工作,因为该函数void*作为参数获取,并且强制转换std::atomic<type>为有效void*
在 Linux 上,不清楚是否可以std::atomic与futex. futex得到无论是uint32_t*或int32_t*(取决于你读这手册),铸造std::atomic<u/int>于u/int*为UB。另一方面,手册说
uaddr 参数指向 futex 字。 在所有平台上,futex 都是四字节整数,必须在四字节边界上对齐。在 futex_op 参数中指定要对 futex 执行的操作;val 是一个值,其含义和目的取决于 futex_op。
提示alignas(4) std::atomic<int>应该有效,只要类型具有 4 个字节的大小和 4 的对齐方式,它是哪种整数类型并不重要。
此外,我已经看到很多地方实现了这种结合原子和 futexes 的技巧,包括boost和TBB。
那么以非 …