C++20 包括atomic<float>和的专门化atomic<double>。这里有人能解释一下这有什么实际用途吗?我能想象的唯一目的是,当我有一个线程在随机点异步更改原子双精度或浮点并且其他线程异步读取该值时(但易失性双精度或浮点实际上应该在大多数平台上执行相同的操作)。但这种需要应该是极其罕见的。我认为这种罕见的情况不能证明纳入 C++20 标准是合理的。
是什么设计目的或者技术限制导致返回值std::fetch_add变成了之前的值?
我知道 std::atomic 应该具有明确定义的行为,但我找不到这个问题的易于理解的在线答案:Do std::atomic.load() 和 .store() 有执行保证吗?
如果两个线程尝试对同一个 std::atomic 对象进行并发写入或读取,是否保证写入和读取都被执行?换句话说,写入或读取任务是否有可能根本无法完成?其中之一或两者会被阻止吗?或者它们保证是连续的?我不是在这里询问操作顺序。我只是简单地询问手术是否会在未来某个未指定的时间进行。
从 C++20 std::atomics 开始,有等待和通知操作。使用 is_always_lock_free 我们可以确保实现是无锁的。使用这些积木构建无锁互斥锁并不那么困难。在简单的情况下,锁定将是比较交换操作,或者如果互斥体被锁定则等待。这里最大的问题是这是否值得。如果我可以创建这样的实现,那么 STL 版本很可能会更好、更快。然而,我仍然记得当我在 2016 年看到 QMutex 如何优于 std::mutex QMutex 与 std::mutex时,我是多么惊讶。那么您认为我应该尝试这样的实现还是 std::mutex 的当前实现已经足够成熟,可以进行远远超出这些技巧的优化?
更新 我的措辞不是最好的,我的意思是实现可以在快乐的路径上无锁(从未锁定状态锁定)。当然,如果我们需要等待获取锁,我们应该被阻塞并重新调度。在大多数平台上,atomic::wait 很可能不是通过简单的自旋锁实现的(现在让我们忽略极端情况),所以基本上它实现了 mutex::lock 所做的相同的事情。所以基本上,如果我实现这样一个类,它将执行与 std::mutex 完全相同的操作(同样在大多数流行平台上)。这意味着 STL 可以在支持这些技巧的平台上的互斥实现中使用相同的技巧。就像这个 spinlock,但我会使用原子等待而不是旋转。我应该相信我的 STL 实现是他们这样做的吗?
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
x.store(true,std::memory_order_seq_cst); // 1
}
void write_y()
{
y.store(true,std::memory_order_seq_cst); // 2
}
void read_x_then_y()
{
while(!x.load(std::memory_order_seq_cst)); // 3
if(y.load(std::memory_order_seq_cst)) // 4
++z;
}
void read_y_then_x()
{
while(!y.load(std::memory_order_seq_cst)); // 5
if(x.load(std::memory_order_seq_cst)) // 6
++z;
}
int main() {
x=false;
y=false;
z=0;
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join();
b.join();
c.join();
d.join();
assert(z.load()!=0);
}
Run Code Online (Sandbox Code Playgroud)
在《C++ Concurrency in Action》一书中,作者在谈论顺序一致性时给出了这个例子,并说assert永远不能触发,因为
[1] 或 [2] 必须首先发生...并且如果一个线程看到 x==true 然后随后看到 y==false,这意味着按照总顺序,对 …
#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_seq_cst); // #1
}
void write_y()
{
y.store(true, std::memory_order_seq_cst); // #2
}
void read_x_then_y()
{
while (!x.load(std::memory_order_seq_cst)) // #3
;
if (y.load(std::memory_order_seq_cst)) { // #4
++z;
}
}
void read_y_then_x()
{
while (!y.load(std::memory_order_seq_cst)) // #5
;
if (x.load(std::memory_order_seq_cst)) { // #6
++z;
}
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x); …Run Code Online (Sandbox Code Playgroud) 原子是否将可重复读取缓存在寄存器中?或者它们只是原子的,即读取可能不会分成多个部分?
MSVC++、clang++ / clang-cl 和 g++ 不会在没有内存排序的情况下缓存原子读取:
#include <atomic>
using namespace std;
int x( atomic_int const &ai )
{
int
a = ai.load( memory_order_relaxed ),
b = ai.load( memory_order_relaxed );
return a + b;
}
Run Code Online (Sandbox Code Playgroud)
克++:
movl (%rdi), %edx
movl (%rdi), %eax
addl %edx, %eax
ret
Run Code Online (Sandbox Code Playgroud)
铿锵-cl:
mov eax, dword ptr [rcx]
add eax, dword ptr [rcx]
ret
Run Code Online (Sandbox Code Playgroud)
CL:
mov edx, DWORD PTR [rcx]
mov eax, DWORD PTR [rcx]
add eax, edx
ret 0`
Run Code Online (Sandbox Code Playgroud) 目前正在使用 GCC 查看 C/C++ 中的原子操作,发现内存中自然对齐的全局变量具有原子读取和写入。
然而,我试图按位与一个全局变量,并注意到它归结为一个读取-修改-写入序列,如果有多个线程对该字节值进行操作,那么这会很麻烦。
经过一番研究,我选择了这两个例子:
C 示例- GCC 扩展__sync_fetch_and_and
#include <stdio.h>
#include <stdint.h>
uint8_t byteC = 0xFF;
int main() {
__sync_fetch_and_and(&byteC, 0xF0);
printf("Value of byteC: 0x%X\n", byteC);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
C++ 示例- 使用原子的 C++11fetch_and
#include <iostream>
#include <atomic>
std::atomic<uint8_t> byteCpp(0xFF);
int main() {
byteCpp.fetch_and(0xF0);
std::cout << "Value of byteCpp: 0x" << std::hex << static_cast<int>(byteCpp.load()) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
其他示例如下,但它们似乎不太直观且计算成本更高。
用一个pthread_mutex_lock
uint8_t byte = 0xFF;
pthread_mutex_t byte_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&byte_mutex);
byte …Run Code Online (Sandbox Code Playgroud) boost::atomic 要求 T 可以轻松复制。但为什么下面的代码有效呢boost::atomic<atom>?
#include <chrono>
#include <iostream>
#include <memory>
#include <unordered_set>
#include <parallel/algorithm>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/atomic.hpp>
#include <boost/optional/optional.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/algorithm/remove_if.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <boost/range/algorithm/unique.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/numeric.hpp>
typedef uint32_t vint; // Integer for vertex IDs
constexpr vint vmax = std::numeric_limits<vint>::max(); // Used as invalid ID
struct atom {
boost::atomic<float> str; // Total weighted degree of the community members
boost::atomic<vint> child; // Last vertex that is merged to this vertex
std::string c; …Run Code Online (Sandbox Code Playgroud) 鉴于 x86 具有强大的内存模型,是否有必要使用std::memory_order_acquirefence(不是 operation)?
例如,如果我有这个代码:
uint32_t read_shm(const uint64_t offset) {
// m_head_memory_location is char* pointing to the beginning of a mmap-ed named shared memory segment
// a different process on different core will write to it.
return *(uint32_t*)(m_head_memory_location + offset);
}
....
int main() {
uint32_t val = 0;
while (0 != (val = shm.read(some location)));
.... // use val
}
Run Code Online (Sandbox Code Playgroud)
我真的需要std::atomic_thread_fence(std::memory_order_acquire)在return语句之前吗?
我觉得没有必要,因为上面代码的目标是尝试从 读取前 4 个字节m_head_memory_location + offset,因此重新排序栅栏后的任何内存操作都不会影响结果。
或者有一些副作用使获取栅栏变得必要?
在 x86 上是否需要获取栅栏(不是操作)? …