下面的代码是来自https://www.cplusplus.com/reference/atomic/atomic/compare_exchange_weak/的compare_exchange_weak的示例
我不明白 Compare_exchange_weak while 循环内可以接受哪些类型的操作。
在示例中,只要compare_exchange 返回 false,他们就会将 newNode->next 值设置为 oldHead 指针。我不明白这怎么总是有效的。
如果另一个线程处于同一个循环中,并且它在我们设置 oldHead 指针的时间和在我们的线程上compare_exchange 成功的时间之间成功更改了 oldHead 指针,会发生什么情况?那么我们的 newNode 中就会有错误的头指针。我不明白为什么这是不可能的。
例如,如果我在设置 ->next 值后放置 sleep(5),或者在循环中进行一些长计算,这会起作用吗?
标记为“在这里可以安全做什么”的循环是我不明白的部分。
// atomic::compare_exchange_weak example:
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct Node { int value; Node* next; };
std::atomic<Node*> list_head (nullptr);
void append (int val) { // append an element to the list
Node* oldHead = list_head;
Node* newNode …Run Code Online (Sandbox Code Playgroud) 参考下面的代码
auto x = std::atomic<std::uint64_t>{0};
auto y = std::atomic<std::uint64_t>{0};
// thread 1
x.store(1, std::memory_order_release);
auto one = y.load(std::memory_order_seq_cst);
// thread 2
y.fetch_add(1, std::memory_order_seq_cst);
auto two = x.load(std::memory_order_seq_cst);
Run Code Online (Sandbox Code Playgroud)
这里有可能one和two都为 0 吗?
(我似乎遇到了一个错误,在上面的代码运行后,如果one和two都可以保持 0 的值,则可以解释该错误。并且排序规则太复杂,我无法弄清楚上面可以进行哪些排序。)
读完“行动中的并发”后,我找不到一个问题的答案 - 当我们在一个原子变量上有一个存储(释放)和许多加载(获取)时,读取副作用的标准是否有保证?假设我们有:
int i{};
atomic<bool> b{};
void writer(){
i=42;
b.store(true,memory_order_release);
}
void reader(){
while(!b.load(memory_order_acquire))
this_thread::yield();
assert(i==42);
}
//---------------------
thread t1{writer},t2{reader},t3{reader};
Run Code Online (Sandbox Code Playgroud)
如果我们只有一个读取器,一切都可以,但是我们是否可以在 t2 或 t3 线程中断言失败?
我有一个全局引用计数对象obj,我想通过使用原子操作来防止数据争用:
T* obj; // initially nullptr
std::atomic<int> count; // initially zero
Run Code Online (Sandbox Code Playgroud)
std::memory_order_release我的理解是,我需要在写入后使用obj,以便其他线程知道它被创建:
void increment()
{
if (count.load(std::memory_order_relaxed) == 0)
obj = std::make_unique<T>();
count.fetch_add(1, std::memory_order_release);
}
Run Code Online (Sandbox Code Playgroud)
同样,我需要std::memory_order_acquire在读取计数器时使用,以确保线程具有obj被更改的可见性:
void decrement()
{
count.fetch_sub(1, std::memory_order_relaxed);
if (count.load(std::memory_order_acquire) == 0)
obj.reset();
}
Run Code Online (Sandbox Code Playgroud)
我不相信上面的代码是正确的,但我也不完全确定为什么。我觉得obj.reset()调用之后,应该有一个std::memory_order_release操作来通知其他线程。那是对的吗?
是否还有其他可能出错的事情,或者在这种情况下我对原子操作的理解完全错误?
(编辑:只是为了澄清:“缓存一致性”的问题是在不使用原子变量的情况下。)
是否有可能(单CPU情况:Windows可以在Intel / AMD / Arm CPU上运行),线程1运行在core-1上存储一个bool变量(例如)并且它保留在L1缓存中,而线程2在 core-n 上运行使用该变量,并且它会查找内存中该变量的另一个副本?
代码示例(为了演示该问题,我们假设这std::atomic_bool只是一个普通的bool):
#include <thread>
#include <atomic>
#include <chrono>
std::atomic_bool g_exit{ false }, g_exited{ false };
using namespace std::chrono_literals;
void fn()
{
while (!g_exit.load(std::memory_order_acquire))
{
// do something (lets say it takes 1-4s, repeatedly)
std::this_thread::sleep_for(1s);
}
g_exited.store(true, std::memory_order_release);
}
int main()
{
std::thread wt(fn);
wt.detach();
// do something (lets say it took 2s)
std::this_thread::sleep_for(2s);
// Exit
g_exit.store(true, std::memory_order_release);
for (int i = 0; i < 5; …Run Code Online (Sandbox Code Playgroud) c++ multithreading cpu-architecture memory-barriers stdatomic
我目前正在深入研究std::atomicsC++ 内存模型。真正对我的思维模型有帮助的是 CPU 的存储和加载缓冲区的概念,它基本上是一个 fifo 队列,用于存储必须写入 L1 缓存或从 L1 缓存读取的数据,至少在英特尔架构中存在。据我了解,原子操作基本上是对 CPU 的指令,可防止包装类型在编译时或运行时撕裂并跨屏障重新排序写入或读取指令。为了说明我的思维模型中的差距,我很快就想出了这个例子:
#include <atomic>
#include <iostream>
#include <thread>
int a;
int b;
int c;
std::atomic<int> x;
int e = 0;
auto thread1() {
while(1) {
a = 3;
b = 5;
c = 1;
x.store(10, std::memory_order::release);
e++;
std::cout << "stored!" << std::endl;
}
}
auto thread2() {
while(1) {
x.load(std::memory_order::acquire);
std::cout << b << std::endl;
}
}
int main() {
[[maybe_unused]] auto t1 = …Run Code Online (Sandbox Code Playgroud) 我正在学习 C++ 多线程编程。我的测试程序很简单,功能就是统计一个原子变量从0到10000000(高一点也没关系)。我不明白为什么当我将线程数从8设置为16后,执行时间反而增加了一些而不是大幅下降。
std::atomic<int> atomicCounter{0};
void addcount(int threadId) {
while(atomicCounter.load() < 10000000) {
atomicCounter.fetch_add(1);
}
}
void test() {
const int maxNumThreads = 8;
// Time::now() is to get the timestamp accurate to ns
auto s_ts = Time::now();
std::vector<std::thread> threads;
for (int i = 0; i < maxNumThreads; i++) {
threads.emplace_back([&]() {
addcount(i);
});
}
// join the threads
for (auto& thread : threads) {
thread.join();
}
threads.clear();
auto e_ts = Time::now();
LOG(INFO) << "Executing time : " << (e_ts …Run Code Online (Sandbox Code Playgroud) 同事和我讨论了一个假想的问题,当一个人想要实现自旋锁互斥std::atomic_flag,但也实现那个自旋锁而不是(真)但作为一个
while(true)
{
cnt=0;
while (cnt<yieldAfterTries)
{
//try to get lock
cnt++;
}
std::this_thread::yield();
// if got lock do work and then break;
}
Run Code Online (Sandbox Code Playgroud)
基本上的想法是线程不能"很长一段时间"阻止其他人,即使它有一个实时优先级,因为它会在一段时间后产生...但是当我看到std :: yield的规范时我很惊讶它是一个建议,不是强制性的.
提供实现的提示,以重新安排线程的执行,允许其他线程运行.
http://en.cppreference.com/w/cpp/thread/yield
那会有问题吗?
我需要一个可以同时读/写的消息deque.只写就好了
std::atomic<std::deque<Message>> queue;
Run Code Online (Sandbox Code Playgroud)
这是一个包装类,它有一个std::deque<Message> queue_和一个std::mutex mutex_as数据成员,并锁定mutex_之前转发任何操作queue_?
在像std :: atomic :: compare_exchange这样的函数中,有一些运行时参数,比如std :: memory_order_release,std :: memory_order_relaxed.(http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange)
我不确定这些内存顺序标志是否保证存在于各种cpu /架构中,如果某些cpus不支持标志,这个标志是否会导致崩溃或?似乎这些标志中的一些是为intel itanium等设计的,不确定std :: memory_order相关代码是否可移植.
你能提一些建议吗?
stdatomic ×10
c++ ×9
atomic ×3
c++11 ×3
memory-model ×3
c++20 ×1
concurrency ×1
portability ×1
stdthread ×1