如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
我读了一章,我不喜欢它.我还不清楚每个内存顺序之间的差异.这是我目前的推测,我在阅读更简单的http://en.cppreference.com/w/cpp/atomic/memory_order后理解这一点.
以下是错误的,所以不要试图从中学习
a在另一个线程上放宽了存储.我b用seq_cst 存储.第三个线程读取a 放松将看到变化以及b任何其他原子变量? ).如果我错了,我想我理解但是纠正我.我找不到任何用易于阅读的英语解释它的东西.
(这是重复:如何正确读取Interlocked.Increment'ed int字段?但是,在阅读了答案和评论之后,我仍然不确定正确的答案.)
有些代码我不拥有,也无法更改为使用在几个不同线程中增加int计数器(numberOfUpdates)的锁.所有通话都使用:
Interlocked.Increment(ref numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
我想在我的代码中读取numberOfUpdates.既然这是一个int,我知道它不会撕裂.但是,确保我获得最新价值的最佳方法是什么?看起来我的选择是:
int localNumberOfUpdates = Interlocked.CompareExchange(ref numberOfUpdates, 0, 0);
Run Code Online (Sandbox Code Playgroud)
要么
int localNumberOfUpdates = Thread.VolatileRead(numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
两者都有效(无论优化,重新排序,缓存等,都可以提供最新的价值)?一个比另一个更受欢迎吗?还有第三种选择更好吗?
Interlocked.Exchange和Volatile.Write有什么区别?
两种方法都更新某些变量的值.有人可以总结何时使用它们?
http://msdn.microsoft.com/ru-ru/library/bb337971 和http://msdn.microsoft.com/en-us/library/gg712713.aspx
特别是我需要更新我的数组的双项,我希望另一个线程看到最新的值.什么是首选?Interlocked.Exchange(ref arr[3], myValue)或Volatile.Write(ref arr[3], info);在那里arr被声明为double?
================================================== ==========================真实的例子,我声明了这样的双数组:
private double[] _cachedProduct;
Run Code Online (Sandbox Code Playgroud)
在一个线程中,我更新它:
_cachedProduct[instrumentId] = calcValue;
...
are.Set();
Run Code Online (Sandbox Code Playgroud)
在另一个线程中,我像这样读取这个数组:
while(true) {
are.WaitOne();
...
result += _cachedProduct[instrumentId];
...
}
Run Code Online (Sandbox Code Playgroud)
对我来说它只是工作正常.然而,为了确保"它将永远有效",无论它看起来我应该添加Volatile.Write或Interlocked.Exchange.因为双重更新不能保证是原子的http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx
在这个问题的答案中,我希望看到Volatile和Interlocked类的详细比较.为什么我们需要2节课?哪一个和何时使用?
Herb Sutter 在他的“原子<>武器”演讲中展示了原子的几个示例用途,其中之一可归结为以下内容:(视频链接,带时间戳)
一个主线程启动多个工作线程。
工人检查停止标志:
while (!stop.load(std::memory_order_relaxed))
{
// Do stuff.
}
Run Code Online (Sandbox Code Playgroud)
主线程最终执行此操作stop = true;(注意,使用 order= seq_cst),然后加入工作线程。
Sutter 解释说,使用 order= 检查标志relaxed是可以的,因为谁在乎线程是否会因稍大的延迟而停止。
但为什么要stop = true;在主线程中使用呢seq_cst?幻灯片上说这是故意不这样做relaxed,但没有解释原因。
看起来它会起作用,可能会有更大的停止延迟。
这是性能和其他线程看到标志的速度之间的折衷吗?即,由于主线程仅设置标志一次,我们不妨使用最强的排序,以尽快传达消息?
考虑以下玩具示例,尤其是result函数:
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
class Worker
{
std::thread th;
std::atomic_bool done = false;
int value = 0;
public:
Worker()
: th([&]
{
std::this_thread::sleep_for(std::chrono::seconds(1));
value = 42;
done.store(true, std::memory_order_release);
}) {}
int result() const
{
return done.load(std::memory_order_acquire) ? value : -1;
}
Worker(const Worker &) = delete;
Worker &operator=(const Worker &) = delete;
~Worker()
{
th.join();
}
};
int main()
{
Worker w;
while (true)
{
int r = w.result();
if (r != -1) …Run Code Online (Sandbox Code Playgroud) c++ multithreading atomic micro-optimization memory-barriers
可能重复:
并发:C++ 11内存模型中的原子和易失性
使用C++ 11 <atomic>规范,是否有保证新鲜度?不同内存顺序的描述只涉及重新排序(据我所见).
具体来说,在这种情况下:
#include <atomic>
std::atomic<int> cancel_work(0);
// Thread 1 is executing this function
void thread1_func() {
...
while (cancel_work.load(<some memory order>) == 0) {
...do work...
}
}
// Thread 2 executes this function
void thread2_func() {
...
cancel_work.store(1, <some memory order>);
...
}
Run Code Online (Sandbox Code Playgroud)
如果线程1和线程2不共享任何其他数据cancel_work,那么在我看来,不需要任何排序保证,并且std::memory_order_relax对于存储和加载都是足够的.但这是否能保证线程1能够看到更新,cancel_work而不是只是重复读取其本地缓存线而不从主内存刷新它?如果没有,那么提供保证所需的最低金额是多少?
我一直试图谷歌我的问题,但老实说,我不知道如何简洁地陈述问题.
假设我在多核Intel系统中有两个线程.这些线程在同一个NUMA节点上运行.假设线程1写入X一次,然后只是偶尔读取它向前移动.进一步假设,线程2连续读取X. 如果我不使用内存栅栏,在线程1写入X和线程2看到更新值之间可以有多长时间?
我知道X的写入将转到存储缓冲区并从那里到缓存,此时MESIF将启动,线程2将通过QPI查看更新的值.(或者至少这是我收集到的).我假设存储缓冲区将被写入存储围栏中的缓存或者是否需要重用该存储缓冲区条目,但我不知道存储缓冲区是否已分配给写入.
最终我要为自己回答的问题是,如果线程2有可能在一个相当复杂的应用程序中看到线程1的写入几秒钟而正在做其他工作.
在英特尔优化手册似乎对存储缓冲区的数量存在于处理器的许多地方,但谈判没有谈存储缓冲区的大小.这是公共信息还是商店缓冲区的大小保留为微架构细节?
我正在研究的处理器主要是Broadwell和Skylake,但其他人的信息也不错.
另外,存储缓冲区究竟做了什么?
在 C++ 中,我们有关键字volatile和atomic类。它们之间的区别是,volatile 不保证线程安全的并发读写,而只是保证编译器不会将变量的值存储在缓存中,而是从内存中加载变量,而 atomic 保证线程安全的并发读写。
众所周知,原子读操作是不可分割的,即当一个或多个线程读取变量的值时,两个线程都不能将新值写入变量,所以我认为我们总是读取最新值,但我不确定:)
所以,我的问题是:如果我们声明原子变量,我们是否总是获得变量调用load()操作的最新值?
c++ ×6
atomic ×5
concurrency ×3
c# ×2
c++11 ×2
intel ×2
volatile ×2
x86 ×2
.net ×1
.net-4.5 ×1
assembly ×1
interlocked ×1
lockless ×1
memory-model ×1
performance ×1
stdatomic ×1