如果我在具有原子读取和递增/递减支持的硬件上,我是否可以使用volatile sig_atomic_tC++ 03来访问原子操作并避免使用完整的互斥锁,或者我必须等待C++ 11和std::atomic<int>?
我想从原子 uint32s 拼凑一个 uint64 原子计数器。计数器有一个写入器和多个读取器。编写器是一个信号处理程序,所以它不能阻塞。
我的想法是使用低位的代数作为读锁。读取器重试,直到整个读取过程中生成计数稳定,并且低位未设置。
以下代码在内存排序的设计和使用中是否正确?有没有更好的办法?
using namespace std;
class counter {
atomic<uint32_t> lo_{};
atomic<uint32_t> hi_{};
atomic<uint32_t> gen_{};
uint64_t read() const {
auto acquire = memory_order_acquire;
uint32_t lo, hi, gen1, gen2;
do {
gen1 = gen_.load(acquire);
lo = lo_.load(acquire);
hi = hi_.load(acquire);
gen2 = gen_.load(acquire);
} while (gen1 != gen2 || (gen1 & 1));
return (uint64_t(hi) << 32) | lo;
}
void increment() {
auto release = memory_order_release;
gen_.fetch_add(1, release);
uint32_t newlo = 1 + lo_.fetch_add(1, release);
if (newlo …Run Code Online (Sandbox Code Playgroud) 我不知道为什么我的代码不是线程安全的,因为它输出了一些不一致的结果。
value 48
value 49
value 50
value 54
value 51
value 52
value 53
Run Code Online (Sandbox Code Playgroud)
我对原子对象的理解是,它防止其中间状态暴露出来,因此当一个线程正在读取它而另一线程正在写入它时,它应该解决该问题。
我曾经认为我可以使用没有互斥量的std :: atomic来解决多线程计数器增量问题,但情况并非如此。
我可能误解了原子对象是什么,有人可以解释吗?
value 48
value 49
value 50
value 54
value 51
value 52
value 53
Run Code Online (Sandbox Code Playgroud) 使用std::atomic而不是互斥锁的全部意义在于:
当使用互斥表“模拟”操作的原子性时:
那么为什么对原子 CPU 操作的这种糟糕的模拟值得呢?中非无锁回退机制的用例是std::atomic什么?
请你给点意见好吗?你将做点什么不同的?我的意思是,你认为如果我用 std::task 或 std::mutex、std::condition_variable 等来做会更好吗?我用 2 个标志来控制线程是一种矫枉过正吗?
std::atomic<int> counter = { 0 };
std::atomic<bool> switchFlag = { false };
std::atomic<bool> finished = { false };
constexpr int MAX_NUM = 10;
void increment(){
while (!finished.load()){
if (!switchFlag.load()){
std::cout << "incremented to =" << ++counter << '\n';
switchFlag.store(true);
}
}
}
void print(){
while (!finished.load()) {
if (switchFlag.load()){
std::cout << "counter=" << counter.load() << '\n';
if (counter.load() >= MAX_NUM)
finished.store(true);
switchFlag.store(false);
}
}
}
int main() {
auto t1 = std::thread(increment);
auto …Run Code Online (Sandbox Code Playgroud) 16 字节atomic<>变量是否在 16 字节边界上自动对齐,从而允许编译器/运行时库有效地使用 x86CMPXCHG16B指令?或者我们应该根据风格总是手动指定alignas(16)所有这些变量?
我似乎无法在商店之后获得原子对象的地址。
例如
std::atomic<int> i;
std::atomic<int>* p = &++i; // doesn't work
auto* p = &++i; // doesn't work
// below works:
++i;
auto* p = &i;
Run Code Online (Sandbox Code Playgroud)
这里发生了什么,为什么?
澄清一下:我知道它返回一个 r 值。为什么它不返回原始对象,this?这是一个有目的的设计选择还是一个疏忽?
更具体地说,这个要求的幕后发生了什么?
我了解到线程之间通信的一种方法是共享一些原子数据结构。例如:
struct Point {
int const x, y;
};
std::atomic<Point> some_point_in_shared_memory{Point{0, 0}};
Run Code Online (Sandbox Code Playgroud)
尽管Point::operator=(Point const &)被删除,但调用赋值运算符似乎没有问题std::atomic<Point>,如下所示:
some_point_in_shared_memory = Point{1, 2};
Run Code Online (Sandbox Code Playgroud)
如何实施此操作?
我可能会想到的一种解决方案是使用placement new在旧对象之上构造一个新对象,但显然它不是异常安全的。还是因为Point可以简单地复制而没关系?
根据cppreference,c++20 具有丰富的(对我来说很有用)的atomic_flag操作支持。
但是,尚不清楚 gcc 是否还支持这些功能,在gnu 的功能摘要中找不到它们。我目前正在使用带有-c++=2aset 的版本 8 。
这段代码不能用 GCC8 编译:
#include <atomic>
int main() {
std::atomic_flag myFlag = ATOMIC_FLAG_INIT;
myFlag.test();
}
Run Code Online (Sandbox Code Playgroud)
error: ‘struct std::atomic_flag’ has no member named ‘test’
我不想通过安装较新版本的 g++ 来破坏我的构建环境,并且感谢任何可以报告atomic_flag版本 10 或更高版本支持的人。
考虑两个线程,T1 和 T2,它们分别存储和加载一个原子整数 a_i。让我们进一步假设,这家店在执行前正在执行的负荷启动。以前,我的意思是绝对的时间意义。
T1 T2
// other_instructions here... // ...
a_i.store(7, memory_order_relaxed) // other instructions here
// other instructions here // ...
a_i.load(memory_order_relaxed)
// other instructions here
Run Code Online (Sandbox Code Playgroud)
是否保证T2在加载后看到值7?