例如,您可以安全地递增和递减 std::atomic_int 。但是,如果您需要检查溢出或根据该值有条件地执行某些例程,那么无论如何都需要锁。由于您必须比较该值,并且在比较成功后线程可能会被交换,另一个线程会修改,...bug。
但如果你需要锁,那么你可以使用普通整数而不是原子。我对吗?
首先我想谈一下我对此的一些理解,如有错误请指正。
MFENCE在x86中可以保证全屏障顺序一致性可防止 STORE-STORE、STORE-LOAD、LOAD-STORE 和 LOAD-LOAD 重新排序
这是根据维基百科的说法。
std::memory_order_seq_cst不保证防止 STORE-LOAD 重新排序。
这是根据Alex 的回答,“负载可能会通过早期存储重新排序到不同位置”(对于 x86),并且 mfence 不会总是被添加。
a是否std::memory_order_seq_cst表示顺序一致性?根据第2/3点,我认为这似乎不正确。std::memory_order_seq_cst仅当以下情况时才表示顺序一致性
MFENCE添加到任一LOAD或STORE否则仍有可能重新订购。
根据@LWimsey的评论,我在这里犯了一个错误,如果 和LOAD都是STORE,memory_order_seq_cst则没有重新排序。Alex 可能指出使用非原子或非 SC 的情况。
std::atomic_thread_fence(memory_order_seq_cst)总是产生一个完整的屏障
这是根据Alex的回答。所以我总是可以替换asm volatile("mfence" ::: "memory")为std::atomic_thread_fence(memory_order_seq_cst)
这对我来说很奇怪,因为memory_order_seq_cst原子函数和栅栏函数之间的用法似乎有很大不同。
现在我在MSVC 2015的标准库的头文件中找到这段代码,它实现了std::atomic_thread_fence
inline void _Atomic_thread_fence(memory_order _Order)
{ /* …Run Code Online (Sandbox Code Playgroud) I'm using atomic<> for the first time, and just as using <thread> requires you to link a thread library, it seems like using <atomic> wants you to do... something. What?
> uname -a
Linux sdclxd00239 3.10.0-693.11.6.el7.x86_64 #1 SMP Thu Dec 28 14:23:39 EST 2017 x86_64 x86_64 x86_64 GNU/Linu
> g++ Foo.cxx -g -o MsgQueueNoLock -pthread
/tmp/ccnGOKUG.o: In function `std::atomic<Ptr_T>::store(Ptr_T, std::memory_order)':
/opt/rh/devtoolset-7/root/usr/include/c++/7/atomic:239: undefined reference to `__atomic_store_16'
/tmp/ccnGOKUG.o: In function `std::atomic<Ptr_T>::load(std::memory_order) const':
/opt/rh/devtoolset-7/root/usr/include/c++/7/atomic:250: undefined reference to `__atomic_load_16'
collect2: error: ld returned 1 …Run Code Online (Sandbox Code Playgroud) stdatomic.h似乎包含 atomic_uint_least16_tand atomic_uint_fast16_t,它们是and类型的_Atomic版本,但它不包含. 为什么?stdint.h uint_least16_tuint_fast16_tatomic_uint16_t
有关N1548 草案的一些背景信息:
7.18.1.1 精确宽度整数类型
1 typedef 名称
intN_t指定宽度为 N、无填充位和二进制补码表示的有符号整数类型。因此,int8_t表示具有恰好 8 位宽度的这种有符号整数类型。2 typedef 名称
uintN_t指定宽度为 N 且无填充位的无符号整数类型。因此,uint24_t表示具有恰好 24 位宽度的这种无符号整数类型。3 这些类型是可选的。但是,如果实现提供宽度为 8、16、32 或 64 位、无填充位且(对于有符号类型)具有二进制补码表示的整数类型,则应定义相应的 typedef 名称。
7.18.1.2 最小宽度整数类型
1 typedef 名称
int_leastN_t指定宽度至少为 N 的有符号整数类型,这样尺寸较小的有符号整数类型都不会至少具有指定的宽度。因此,int_least32_t表示宽度至少为 32 位的有符号整数类型。2 typedef 名称
uint_leastN_t指定宽度至少为 N 的无符号整数类型,因此没有较小尺寸的无符号整数类型至少具有指定的宽度。因此,uint_least16_t表示宽度至少为 16 …
假设我有多个线程访问相同的内存位置。而且,如果有的话,它们都写入相同的值,但没有人读取它。之后,所有线程(通过锁)收敛,然后我才读取值。我需要为此使用原子吗?这是针对 x86_64 系统的。该值是一个 int32。
正如我从测试用例中看到的:https : //godbolt.org/z/K477q1
生成的程序集加载/存储原子放松与普通变量相同:ldr 和 str
那么,松弛原子变量和普通变量之间有什么区别吗?
似乎要使用 CMPXCHG16B,必须定义 _STD_ATOMIC_ALWAYS_USE_CMPXCHG16B = 1 以便使用这些指令。
为什么这是默认设置?除非我阅读整个 atomic.h 标头,否则我永远不会发现这一点。
STL 中还有哪些其他全局定义?是否有一份清单可供审查,以便人们能够可靠地了解这些实施细节?
我读过https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
以原子方式将 *this 的对象表示形式 (C++20 之前) 值表示形式 (C++20 起) 与预期的表示形式进行比较,如果它们按位相等,则将前者替换为所需的(执行读取-修改-写入操作) )。否则,将 *this 中存储的实际值加载到预期中(执行加载操作)。
据我了解,代码如下
bool expected=true;
extern atomic<bool> b = false;
void foo ()
{
//
while(!b.compare_exchange_weak(expected, false));
//
}
Run Code Online (Sandbox Code Playgroud)
循环运行一次后(忽略虚假失败)它将失败,并将写入预期false,因此在第二次迭代时,compare_exchange_weak将返回成功,尽管b尚未更改为true。但这一切有什么意义呢?我虽然可以用它作为同步锁,等待其他线程更改b,但现在我想不出它的用法。
cppreference 中的示例还表明,两次调用compare_exchange_strong 后将会成功。
#include <atomic>
#include <iostream>
std::atomic<int> ai;
int tst_val= 4;
int new_val= 5;
bool exchanged= false;
void valsout()
{
std::cout << "ai= " << ai
<< " tst_val= " << tst_val
<< " new_val= " << new_val
<< " exchanged= …Run Code Online (Sandbox Code Playgroud) 根据C++标准:
\n\n\n如果 A 的值用作 B 的操作数,则求值 A 具有对求值 B 的依赖性,除非:
\n\xe2\x80\x94 B 是 std::kill_dependency (29.3) 的任何特化的调用,或者
\n\xe2\x80\x94 A 是内置逻辑 AND(&&,参见 5.14)或逻辑 OR(||,参见 5.15)运算符的左操作数,或者
\n\xe2\x80\x94 A 是条件(?:,参见 5.16)运算符的左操作数,或者
\n\xe2\x80\x94 A 是内置逗号 (,) 运算符的左操作数 (5.18);(...)
\n
我可以理解为什么关系之前排序的依赖项会在kill_dependency调用时停止,但是为什么逻辑AND、OR、逗号等运算符也会破坏依赖链?
\n这是否意味着下面的代码有未定义的行为?
\n//thread1\nint y = 2\natomicVal.store(true);\n\n//thread2 \nauto x = atomicVal.load(std::memory_order_consume);\ncout << x && y;\nRun Code Online (Sandbox Code Playgroud)\n 在无锁的queue.pop()中,我在与循环内的原子获取同步后读取了一个trivialy_copyable变量(整型)。\n最小化的伪代码:
\n//somewhere else writePosition.store(...,release)\n\nbool pop(size_t & returnValue){\nwritePosition = writePosition.load(aquire)\noldReadPosition = readPosition.load(relaxed)\nsize_t value{};\ndo{\n value = data[oldReadPosition]\n newReadPosition = oldReadPosition+1\n}while(readPosition.compare_exchange(oldReadPosition, newReadPosition, relaxed)\n// here we are owner of the value\nreturnValue = value;\nreturn true;\n}\nRun Code Online (Sandbox Code Playgroud)\ndata[oldReadPosition]仅当该值之前从另一个线程读取时,才能更改内存。
读写位置都是 ABA 安全的。\n通过简单的复制,value = data[oldReadPosition]内存data[oldReadPosition]不会被改变。
但是写入线程queue.push(...)可以在读取时更改data[oldReadPosition],前提是另一个线程已经读取了 oldPosition 并更改了 readPosition。
如果您使用该值,这将是一个竞争条件,但是当我们保持value不变时,它是否也是一个竞争条件,从而导致未定义的行为?标准不够具体或者我不\xc2\xb4不理解它。\nimo,这应该是可能的,因为它没有效果。\n我会很高兴得到一个合格的答案以获得更深入的见解
多谢
\n