在看到 Herb Sutters关于“原子武器”的精彩演讲后,我对放松原子的例子感到有些困惑。
我认为C++ 内存模型(SC-DRF = Sequentially Consistent for Data Race Free)中的原子在加载/读取时执行“获取”。
我知道对于负载 [和存储] 是默认值std::memory_order_seq_cst,因此两者是相同的:
myatomic.load(); // (1)
myatomic.load(std::memory_order_seq_cst); // (2)
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好,没有涉及放松的原子(在听完演讲后,我永远不会使用放松的原子。永远。承诺。但当有人问我时,我可能不得不解释......)。
但是为什么我使用时它是“宽松”的语义
myatomic.load(std::memory_order_acquire); // (3)
Run Code Online (Sandbox Code Playgroud)
既然负载是获取而不是释放,为什么这与(1)和不同(2)?究竟是在这里放松?
我唯一能想到的就是我误解了load意味着acquire。如果这是真的,并且默认值seq_cst意味着两者,那不就意味着一个完整的围栏 - 没有任何东西可以传递该指令,也不能传递?我一定误解了那部分。
[并且对称地用于存储和释放]。
我最近升级到了 C++11 兼容编译器,并且尝试将一些代码从 boost 更新到 c++11 标准。我在使用atomic_store转换一些代码时遇到了问题。这是一些简单的测试代码,似乎会引发编译器错误。
int main()
{
std::shared_ptr<int> m = std::make_shared<int>();
*m = 1;
std::shared_ptr<int> a = std::make_shared<int>();
*a = 2;
std::atomic_store(&m,std::move(a));
std::cout << *m << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
该std::atomic_store(&m,std::move(a));行为我抛出编译器错误:
'std::shared_ptr<int>' is not derived from 'volatile std::atomic<_ITp>'
std::atomic_store(&m,std::move(a));
^
Run Code Online (Sandbox Code Playgroud)
从 boost 迁移到 C++11 时,atomic_store 的方式是否发生了变化?我现在需要创建共享指针的原子对象吗?
假设我让atomic<int> i;线程 A 使用 memory_order_release 执行原子存储/交换。接下来,线程 B 使用 memory_order_release 执行原子存储。线程 C 执行原子 fetch_add(0, memory_order_acquire);
线程 C 是否从线程A 和 B或仅从线程 B获取依赖项?
我正在开发一个处理大量原子操作的项目。到目前为止,我还不知道\xe2\x80\x99t atomic_load(),并且仅依靠赋值运算符来获取原子类型的值,并且除了这么多测试之外,我还没有\xe2\x80\x99t看到错误。这些原子类型会被多个进程和线程以及 更改atomic_compare_exchange_strong_explicit(),因此它们每次都需要一个旧值,而 \xe2\x80\x99s 我总是这样做,oldValue = <Atomic_ type_variable>并且它总是工作正常。\n这只是偶然吗?我应该更喜欢使用atomic_load()吗?
我有十几个线程读取指针,并且一个线程可能每小时左右更改该指针一次。
读者对时间超级、超级、超级敏感。我听说这个atomic<char**>或其他什么是进入主内存的速度,我想避免这种情况。
在现代(比如2012年及以后)服务器和高端桌面Intel中,8字节对齐的正则指针能否保证正常读写时不会撕裂?我的测试运行了一个小时,没有看到撕裂。
否则,如果我以原子方式进行写入并正常读取,会更好(或更糟)吗?例如通过将两者结合起来?
请注意,还有其他有关混合原子和非原子操作的问题,这些问题没有指定 CPU,并且讨论会转向语言法律主义。这个问题与规范无关,而是关于到底会发生什么,包括我们是否知道在未定义规范的情况下会发生什么。
我想在原子和非原子操作之间使用独立的内存屏障(我认为无论如何它都不重要)。我想我了解存储屏障和加载屏障的含义以及 4 种可能的内存重新排序;LoadLoad, StoreStore, LoadStore, StoreLoad.
但是,我总是发现获取/释放概念令人困惑。因为在阅读文档时,acquire 不仅说到loads,还说到stores,而release 不仅说到stores,还说到loads。另一方面,普通负载障碍仅为您提供负载保证,而普通商店障碍仅为您提供商店保证。
我的问题如下。在 C11/C++11 中,将独立atomic_thread_fence(memory_order_acquire)视为负载屏障(防止LoadLoad重新排序)和atomic_thread_fence(memory_order_release)存储屏障(防止StoreStore重新排序)是否安全?
如果以上是正确的,我可以用什么来防止LoadStore和StoreLoad重新排序?
当然,我对可移植性感兴趣,我不在乎上述在特定平台上产生什么。
我在p0019r8上读到以下内容:
Run Code Online (Sandbox Code Playgroud)atomic_ref(T& obj);要求:引用的对象应与 对齐
required_alignment。
当未对齐时, cppreference将其解释为 UB:
如果 obj 未与 required_alignment 对齐,则行为未定义。
那么您期望实现如何处理它呢?
并且实现可以检查编译时 alignof,但实际上类型可能比对齐更对齐alignof。实现可以解释指针位并检查运行时对齐,但这是额外的运行时检查。
最终我看到以下选项:
alignof) 并在错误时发出警告alignof) ,如果错误不正确,则编译时失败,alignof),如果错误不正确,则在运行时失败,alignof) 并在错误时回退到基于锁
int main(){
atomic<bool> atomic_lock(false);
std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
int count = 0;
auto f = [&](){
bool flag = false;
for( int i = 0; i< 10000000; ++i){
while(!atomic_lock.compare_exchange_strong(flag, true)){}
//while(lock_flag.test_and_set(std::memory_order_seq_cst));
++count;
//lock_flag.clear(std::memory_order_seq_cst);
atomic_lock.store(false, std::memory_order_seq_cst);
}
};
thread t1(f);
thread t2(f);
t1.join();
t2.join();
cout<<count<<endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是我的程序,我想用 CAS 替换互斥锁,但不是 20000000 的输出表明它不是线程安全程序,哪里错了?但是,我用 atomic_flag 替换 atomic 显示如上,输出是正确的
根据C++ Reference,mutex.lock()是一个memory_order_acquire操作,并且mutex.unlock()是一个memory_order_release操作。
然而,memory_order_acquire和memory_order_release只对非原子和宽松原子操作有效。
memory_order:释放-获取cppreference 上的排序
如果线程 A 中的原子存储被标记
memory_order_release,并且线程 B 中来自同一变量的原子加载被标记memory_order_acquire,则从线程 A 的角度来看,所有内存写入(非原子和宽松原子)发生在原子存储之前,在线程 B 中变得可见的副作用
C++中的互斥体能否保证原子操作的可见性?示例如下。代码可以A在 , 之前重新排序mu.lock(),并且线程b读x为 吗false?
#include <thread>
#include <atomic>
#include <cassert>
#include <iostream>
#include <unistd.h>
std::atomic<bool> x = {false};
std::mutex mu;
void write_x(){
mu.lock();
std::cout << "write_x" << std::endl;
x.store(true, std::memory_order_release);
mu.unlock();
}
void read_x() {
mu.lock();
std::cout …Run Code Online (Sandbox Code Playgroud) 这是我的测试代码
class bar
{
public:
explicit bar(int x) : num(x) {}
int get_num()
{
return num;
}
private:
int num;
};
shared_ptr<bar> ptr_store;
void get_func()
{
while (1)
printf("get_num:%d\n", ptr_store->get_num());
};
void set_func()
{
while (1)
//ptr_store = make_shared<bar>(1);
atomic_exchange(&ptr_store, make_shared<bar>(1));
}
int main()
{
ptr_store = make_shared<bar>(-1);
std::thread t1(get_func);
std::thread t2(set_func);
t1.join();
t2.join();
}
Run Code Online (Sandbox Code Playgroud)
我想知道为什么这个程序不会进行核心转储?
如果set_func在使用时t2破坏原点,是否会导致某些故障?是否由 保证?或者这只是一个巧合。ptr_storet1ptr_store->get_num()shared_ptr
Test environment:
OS: Ubuntu 20.04 LTS
Clang: clang version 3.9.1
G++: gcc version 7.5.0 …Run Code Online (Sandbox Code Playgroud) stdatomic ×10
c++ ×9
c++11 ×4
memory-model ×3
atomic ×2
atomicity ×1
c ×1
c++20 ×1
c11 ×1
mutex ×1
performance ×1
rvalue ×1
shared-ptr ×1
x86-64 ×1