如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
我编写了一个简单的多线程程序,如下所示:
static bool finished = false;
int func()
{
size_t i = 0;
while (!finished)
++i;
return i;
}
int main()
{
auto result=std::async(std::launch::async, func);
std::this_thread::sleep_for(std::chrono::seconds(1));
finished=true;
std::cout<<"result ="<<result.get();
std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它通常表现在调试模式下在Visual Studio中或-O0在GC c和后打印出的结果1秒钟。但是它卡住了,在“ 释放”模式或中不打印任何内容-O1 -O2 -O3。
任何人都可以用简单的英语解释什么是std :: memory_order,以及如何将它们与std :: atomic <>一起使用?
我在这里找到了参考文献和一些例子,但根本不了解. http://en.cppreference.com/w/cpp/atomic/memory_order
谢谢.
存储是释放操作,负载是两者的获取操作.我知道这memory_order_seq_cst意味着要为所有操作强加一个额外的总排序,但是我没有建立一个例子,如果所有的操作memory_order_seq_cst都被替换,那就不是这样了memory_order_acq_rel.
我是否会遗漏某些内容,或者差异仅仅是文档效果,即memory_order_seq_cst如果有人打算不使用更轻松的模型并且memory_order_acq_rel在约束轻松模型时使用,则应该使用?
假设对齐的指针加载和存储在目标平台上自然是原子的,这有什么区别:
// Case 1: Dumb pointer, manual fence
int* ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr = new int(-4);
Run Code Online (Sandbox Code Playgroud)
这个:
// Case 2: atomic var, automatic fence
std::atomic<int*> ptr;
// ...
ptr.store(new int(-4), std::memory_order_release);
Run Code Online (Sandbox Code Playgroud)
还有这个:
// Case 3: atomic var, manual fence
std::atomic<int*> ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr.store(new int(-4), std::memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)
我的印象是它们都是等价的,但是Relacy在第一种情况下(仅)检测到数据竞争:
struct test_relacy_behaviour : public rl::test_suite<test_relacy_behaviour, 2>
{
rl::var<std::string*> ptr;
rl::var<int> data;
void before()
{
ptr($) = nullptr;
rl::atomic_thread_fence(rl::memory_order_seq_cst);
}
void thread(unsigned int id)
{
if (id == 0) { …Run Code Online (Sandbox Code Playgroud) 该标准定义了几个“发生在之前”关系,这些关系将古老的“之前排序”扩展到多个线程:
\n\n\n
[intro.races]11 评估 A简单地发生在评估 B 之前,如果
\n(11.1) \xe2\x80\x94 A 在 B 之前排序,或者
\n
\n(11.2) \xe2\x80\x94 A 与 B 同步,或者
\n(11.3) \xe2\x80\x94 A 只是发生在 X 之前,并且X 只是发生在 B 之前。[注10:在没有消耗操作的情况下,发生在之前和简单发生在关系之前是相同的。\xe2\x80\x94 尾注]
\n12 评估 A强烈发生在评估 D 之前,如果:
\n(12.1) \xe2\x80\x94 A 在 D 之前排序,或者
\n(12.2) \xe2\x80\x94 A 与 D 同步,并且 A 和 D 都是顺序一致的原子操作\n([atomics.order] ),或
\n(12.3) \xe2\x80\x94 存在评估 B 和 C,使得 A 在 B 之前排序,B 仅发生在 C 之前,并且 C 在 D 之前排序,或者 …
有什么区别
extern std::atomic<int> x;
int i = x++;
Run Code Online (Sandbox Code Playgroud)
和
extern std::atomic<int> x;
int i = x.fetch_add(1);
Run Code Online (Sandbox Code Playgroud)
我觉得第二个版本更安全,但我看不出这两个版本之间的测试有什么不同.
这是有关C ++标准的形式保证的问题。
该标准指出,std::memory_order_relaxed原子变量规则允许“凭空” /“出乎意料”的值出现。
但是对于非原子变量,这个例子可以有UB吗?是否r1 == r2 == 42有可能在C ++抽象机?== 42最初都不是变量,因此您不希望任何if主体执行,这意味着不会写入共享变量。
// Global state
int x = 0, y = 0;
// Thread 1:
r1 = x;
if (r1 == 42) y = r1;
// Thread 2:
r2 = y;
if (r2 == 42) x = 42;
Run Code Online (Sandbox Code Playgroud)
上面的示例改编自标准,该标准明确表示原子对象规范允许这种行为:
[注意:在以下示例中,要求确实允许r1 == r2 == 42,而x和y最初为零:
Run Code Online (Sandbox Code Playgroud)// Thread 1: r1 = x.load(memory_order_relaxed); if (r1 == 42) y.store(r1, memory_order_relaxed); // …
TL;DR:在生产者-消费者队列中,放置一个不必要的(从 C++ 内存模型的角度来看)内存栅栏或不必要的强内存顺序是否有必要以牺牲可能更差的吞吐量为代价来获得更好的延迟?
C++ 内存模型是在硬件上执行的,方法是使用某种内存栅栏来实现更强的内存顺序,而不是将它们放在较弱的内存顺序上。
特别是,如果生产者这样做store(memory_order_release),而消费者使用 观察存储的值load(memory_order_acquire),则加载和存储之间没有围栏。在 x86 上根本没有栅栏,在 ARM 上栅栏是在存储之前和加载之后进行放置操作。
没有围栏存储的值最终会被没有围栏的负载观察到(可能在几次不成功的尝试之后)
我想知道在队列的两侧放置围栏是否可以更快地观察到值?如果有围栏和没有围栏,延迟是多少?
我希望只有一个循环load(memory_order_acquire)和pause/yield限制为数千次迭代是最好的选择,因为它无处不在,但想了解原因。
由于这个问题是关于硬件行为的,我希望没有通用的答案。如果是这样,我主要想知道 x86(x64 风格),其次是 ARM。
例子:
T queue[MAX_SIZE]
std::atomic<std::size_t> shared_producer_index;
void producer()
{
std::size_t private_producer_index = 0;
for(;;)
{
private_producer_index++; // Handling rollover and queue full omitted
/* fill data */;
shared_producer_index.store(
private_producer_index, std::memory_order_release);
// Maybe barrier here or stronger order above?
}
}
void consumer()
{
std::size_t private_consumer_index = 0;
for(;;)
{
std::size_t observed_producer_index = shared_producer_index.load( …Run Code Online (Sandbox Code Playgroud)