我想知道为什么没有编译器准备将相同值的连续写入合并到单个原子变量,例如:
#include <atomic>
std::atomic<int> y(0);
void f() {
auto order = std::memory_order_relaxed;
y.store(1, order);
y.store(1, order);
y.store(1, order);
}
Run Code Online (Sandbox Code Playgroud)
我尝试过的每个编译器都会发出三次上面的编写.什么合法的,无种族的观察者可以看到上述代码与具有单次写入的优化版本之间的差异(即,不是"假设"规则适用)?
如果变量是易变的,那么显然不适用优化.在我的情况下有什么阻止它?
这是编译器资源管理器中的代码.
http://en.cppreference.com/w/cpp/atomic/memory_order和其他C++ 11在线参考,将memory_order_acquire和memory_order_release定义为:
这似乎允许在获取操作之前执行获取后写入,这看起来很奇怪(通常的获取/释放操作语义限制所有内存操作的移动).
相同的在线资源(http://en.cppreference.com/w/cpp/atomic/atomic_flag)表明可以使用C++原子和上面提到的宽松内存排序规则构建自旋锁互斥:
lock mutex: while (lock.test_and_set(std::memory_order_acquire))
unlock mutex: lock.clear(std::memory_order_release);
Run Code Online (Sandbox Code Playgroud)
有了这个锁定/解锁的定义,如果确实以这种方式定义了memory_order_acquire/release,那么下面的简单代码就不会被破坏(即,不禁止对获取后写入进行重新排序):
Thread1:
(0) lock
(1) x = 1;
(2) if (x != 1) PANIC
(3) unlock
Thread2:
(4) lock
(5) x = 0;
(6) unlock
Run Code Online (Sandbox Code Playgroud)
以下执行是否可行:(0)锁定,(1)x = 1,(5)x = 0,(2)PANIC?我错过了什么?
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,但没有解释原因。
看起来它会起作用,可能会有更大的停止延迟。
这是性能和其他线程看到标志的速度之间的折衷吗?即,由于主线程仅设置标志一次,我们不妨使用最强的排序,以尽快传达消息?
根据https://en.cppreference.com/w/cpp/atomic/memory_order mutex.lock()和mutex.unlock()是获取和释放操作。获取操作使得无法在它前面重新排序后面的指令。并且释放操作使得在它之后无法重新排序之前的指令。这使得以下代码:
[Thread 1]
mutex1.lock();
mutex1.unlock();
mutex2.lock();
mutex2.unlock();
[Thread 2]
mutex2.lock();
mutex2.unlock();
mutex1.lock();
mutex1.unlock();
Run Code Online (Sandbox Code Playgroud)
可以重新排序为以下(可能是死锁)代码:
[Thread 1]
mutex1.lock();
mutex2.lock();
mutex1.unlock();
mutex2.unlock();
[Thread 2]
mutex2.lock();
mutex1.lock();
mutex2.unlock();
mutex1.unlock();
Run Code Online (Sandbox Code Playgroud)
这种重新排序是否可能发生。或者有什么规则阻止它?
我知道atomic<T>当多个线程正在读取和写入变量时,将会对类型“T”变量应用锁定,确保只有其中一个线程正在执行读/写操作。
但在多CPU核心计算机中,线程可以运行在不同的核心上,不同的核心会有不同的L1-cache、L2-cache,同时共享L3-cache。我们知道有时C++编译器会优化将变量存储在寄存器中,这样如果变量没有存储在内存中,那么该变量上的不同core-cache之间就没有内存同步。
如果atomic<T>编译器将变量优化为某个寄存器变量,那么它不会存储在内存中,当一个核心写入其值时,另一个核心可能会读出陈旧的值,对吗?这个数据一致性有保证吗?