以下代码无法链接:
#include <atomic>
struct A
{
unsigned long a;
unsigned long b;
};
struct B
{
void set(A tmp)
{
_a.store(tmp);
}
std::atomic<A> _a;
};
int main()
{
B b;
b.set(A());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
出现以下错误:
/tmp/cc8gyaZM.o: In function `std::atomic<A>::store(A, std::memory_order)':
dryn.cpp: (.text._ZNSt6atomicI1AE5storeES0_St12memory_order[_ZNSt6atomicI1AE5storeES0_St12memory_order]+0x3e): undefined reference to `__atomic_store_16'
Run Code Online (Sandbox Code Playgroud)
如果我用大小为int的任何东西替换unsigned long-s,那么编译就好了.使用g ++ 4.7.2.你知道为什么会这样吗?
用命令编译:
g++ dryn.cpp --std=c++11
Run Code Online (Sandbox Code Playgroud) 我不明白,release sequence如果我们在下面的例子中有2个线程,为什么会没有问题.我们对原子变量只有2个操作count.count按顺序递减,如输出中所示.
从C++并发在行动由安东尼·威廉姆斯:
我提到你可以
synchronizes-with relationship在一个store原子变量和load另一个线程的原子变量之间得到一个原子变量,即使在和read-modify-write之间有一系列操作,只要所有操作都被适当标记.如果商店标有,或,并且负载标有,或,并且链中的每个操作都加载了前一个操作所写的值,则操作链构成一个释放序列和初始存储(用于或者)或(是)最终负荷.链中的任何原子读 - 修改 - 写操作都可以有任何内存排序(偶数).storeloadmemory_order_releasememory_order_acq_relmemory_order_seq_cstmemory_order_consumememory_order_acquirememory_order_seq_cstsynchronizes-withmemory_order_acquirememory_order_seq_cstdependency-ordered-beforememory_order_consumememory_order_relaxed要查看这意味着什么(发布顺序)及其重要性,请考虑将
atomic<int>其用作共享队列中项目数的计数,如下面的清单所示.处理事情的一种方法是让生成数据的线程将项目存储在共享缓冲区中,然后执行
count.store(number_of_items, memory_order_release)#1以让其他线程知道数据可用.在实际读取共享缓冲区#4之前,消耗队列项的线程可以执行count.fetch_sub(1,memory_ order_acquire)#2以从队列中声明项目.一旦计数变为零,就没有更多的项目,并且线程必须等待#3.
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>
std::vector<int> queue_data;
std::atomic<int> count;
std::mutex m;
void process(int i)
{
std::lock_guard<std::mutex> …Run Code Online (Sandbox Code Playgroud) 几天前,我写了类似下面的内容:
struct A {
std::atomic_bool b = false;
};
Run Code Online (Sandbox Code Playgroud)
使用VC++ 2015编译器在Visual Studio 2015 Update 3中编译,没有出现任何错误.
现在我在Ubuntu上用GCC(5.4.0)重新编译了同样的东西并得到了错误:
使用已删除的函数'std :: atomic :: atomic(const std :: atomic&)
我在ideone上遇到了同样的错误,设置为C++ 14(不确定它使用的是什么编译器版本).
当然将代码更改为以下修复了gcc的问题:
struct A {
std::atomic_bool b { false };
};
Run Code Online (Sandbox Code Playgroud)
我的问题是:
1.谁是正确的(C++ 11兼容),VC++或GCC?似乎VC++从bool调用构造函数,而GCC调用复制构造函数(已删除).
2.为了在类声明中初始化原子的默认值,是正确初始化(上面)正确/首选方式吗?或者我应该使用ATOMIC_VAR_INIT宏(呃!)吗?
struct A {
std::atomic_bool b = ATOMIC_VAR_INIT(false);
};
Run Code Online (Sandbox Code Playgroud) 如何分配具有原子类型的向量成员?
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
int main()
{
vector<atomic<bool>> myvector;
int N=8;
myvector.assign(N,false);
cout<<"done!"<<endl;
}
Run Code Online (Sandbox Code Playgroud)
https://wandbox.org/permlink/lchfOvqyL3YKNivk
prog.cc: In function 'int main()':
prog.cc:11:28: error: no matching function for call to 'std::vector<std::atomic<bool> >::assign(int&, bool)'
11 | myvector.assign(N,false);
| ^
Run Code Online (Sandbox Code Playgroud) 这个问题是对此的跟进/澄清:
MOV x86 指令是否实现了 C++11 memory_order_release 原子存储?
这表明MOV汇编指令足以在 x86 上执行获取-释放语义。我们不需要LOCK,围栏xchg等。但是,我很难理解这是如何工作的。
英特尔文档第 3A 卷第 8 章指出:
https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf
在单处理器(核心)系统中......
- 读取不会与其他读取重新排序。
- 写入不会与较旧的读取重新排序。
- 对内存的写入不会与其他写入重新排序,但以下情况除外:
但这是针对单核的。多核部分似乎没有提到如何强制执行负载:
在多处理器系统中,以下排序原则适用:
- 单个处理器使用与单处理器系统相同的排序原则。
- 所有处理器都以相同的顺序观察单个处理器的写入。
- 来自单个处理器的写入与来自其他处理器的写入无关。
- 记忆排序服从因果关系(记忆排序尊重传递可见性)。
- 除了执行存储的处理器之外的处理器以一致的顺序看到任何两个存储
- 锁定指令具有总顺序。
那么如何才能MOV单独促进获取释放呢?
libc++在方法中std::counting_semaphore使用原子增量:memory_order_releaserelease
void release(ptrdiff_t __update = 1)
{
if(0 < __a.fetch_add(__update, memory_order_release))
;
else if(__update > 1)
__a.notify_all();
else
__a.notify_one();
}
Run Code Online (Sandbox Code Playgroud)
并memory_order_acquire在acquire方法中将交换与成功的内存顺序进行比较:
void acquire()
{
auto const __test_fn = [=]() -> bool {
auto __old = __a.load(memory_order_relaxed);
return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
};
__cxx_atomic_wait(&__a.__a_, __test_fn);
}
Run Code Online (Sandbox Code Playgroud)
使获取与发布同步的明显选择。
但是,C++20 草案说:
Run Code Online (Sandbox Code Playgroud)void release(ptrdiff_t update = 1);...
同步:强烈发生在调用 try_acquire 之前,观察效果的结果。
强发生在比同步之前 …
在新标准 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。
根据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)
等等
后一个版本有什么通用性(前一个版本不通用),为什么需要它们?
考虑以下示例代码,其中线程 A 将函数推送到队列中,线程 B 在从队列中弹出时执行这些函数:
std::atomic<uint32_t> itemCount;
//Executed by thread A
void run(std::function<void()> function) {
if (queue.push(std::move(function))) {
itemCount.fetch_add(1, std::memory_order_acq_rel);
itemCount.notify_one();
}
}
//Executed by thread B
void threadMain(){
std::function<void()> function;
while(true){
if (queue.pop(function)) {
itemCount.fetch_sub(1, std::memory_order_acq_rel);
function();
}else{
itemCount.wait(0, std::memory_order_acquire);
}
}
}
Run Code Online (Sandbox Code Playgroud)
其中queue是一个并发队列,它有一个push和一个pop函数,每个函数返回一个bool指示给定操作是否成功。所以如果满了就push返回,如果空就返回。falsepopfalse
现在我想知道代码是否在所有情况下都是线程安全的。假设线程 Bpop失败并且即将调用std::atomic<T>::wait. 同时,线程 A 推送一个新元素,而线程 B 检查初始等待条件。由于itemCount尚未更改,因此失败。
紧接着,线程 A 增加计数器并尝试通知一个正在等待的线程(尽管线程 B 尚未在内部等待)。线程 B 最终等待原子,导致线程由于丢失信号而永远不会再次醒来,尽管队列中有一个元素。只有当新元素被推入队列时才会停止,通知 …
我有以下代码:
#include <atomic>
int main () {
std::atomic<uint32_t> value(0);
value.fetch_add(1, std::memory_order::relaxed);
static_assert(std::atomic<uint32_t>::is_always_lock_free);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它可以编译,所以这意味着std::atomic<uint32_t>::is_always_lock_free是真的。
然后,使用 gcc 10 和 的汇编代码如下所示-std=c++20 -O3 -mtune=skylake-avx512 -march=skylake-avx512:
0000000000401050 <main>:
401050: c7 44 24 fc 00 00 00 mov DWORD PTR [rsp-0x4],0x0
401057: 00
401058: f0 ff 44 24 fc lock inc DWORD PTR [rsp-0x4]
40105d: 31 c0 xor eax,eax
40105f: c3 ret
Run Code Online (Sandbox Code Playgroud)
许多帖子指出,读-修改-写操作(fetch_add()此处)不能是没有锁的原子操作。
我的问题是std::atomic::is_always_lock_free存在的true真正含义是什么。
该页面说明Equals true if this atomic type is …
stdatomic ×10
c++ ×9
c++11 ×6
atomic ×2
c++20 ×2
gcc ×2
memory-model ×2
atomic-swap ×1
c ×1
intrinsics ×1
lock-free ×1
stdvector ×1
wait ×1
x86 ×1