C++ 11引入了标准化的内存模型,但究竟是什么意思呢?它将如何影响C++编程?
这篇文章(引用Herb Sutter的Gavin Clarke)说,
内存模型意味着C++代码现在有一个标准化的库可以调用,无论是谁编译器以及它运行的平台.有一种标准方法可以控制不同线程与处理器内存的对话方式.
"当你谈论在标准中的不同内核之间分割[代码]时,我们正在谈论内存模型.我们将优化它,而不会破坏人们将在代码中做出的以下假设," Sutter说.
好吧,我可以在网上记住这个和类似的段落(因为我从出生以来就拥有自己的记忆模型:P),甚至可以发布作为其他人提出的问题的答案,但说实话,我并不完全明白这个.
C++程序员以前用于开发多线程应用程序,那么如果它是POSIX线程,Windows线程或C++ 11线程,它又如何重要呢?有什么好处?我想了解低级细节.
我也觉得C++ 11内存模型与C++ 11多线程支持有某种关系,因为我经常将这两者结合在一起.如果是,究竟是怎么回事?他们为什么要相关?
由于我不知道多线程的内部工作原理以及内存模型的含义,请帮助我理解这些概念.:-)
正如我最近发布的这个答案所示,我似乎对volatile多线程编程环境中的实用程序(或缺乏实用程序)感到困惑.
我的理解是这样的:每当一个变量可以在访问它的一段代码的控制流之外被改变时,该变量应该被声明为volatile.信号处理程序,I/O寄存器和由另一个线程修改的变量都构成这种情况.
所以,如果你有一个全局int foo,并且foo由一个线程读取并由另一个线程原子设置(可能使用适当的机器指令),则读取线程看到这种情况的方式与它看到由信号处理程序调整的变量或由外部硬件条件修改,因此foo应该声明volatile(或者,对于多线程情况,使用内存隔离负载访问,这可能是一个更好的解决方案).
我怎么错,哪里错了?
我经常发现这些术语在并发编程的上下文中使用.它们是相同的还是不同的?
我想知道为什么没有编译器准备将相同值的连续写入合并到单个原子变量,例如:
#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后理解这一点.
以下是错误的,所以不要试图从中学习
a在另一个线程上放宽了存储.我b用seq_cst 存储.第三个线程读取a 放松将看到变化以及b任何其他原子变量? ).如果我错了,我想我理解但是纠正我.我找不到任何用易于阅读的英语解释它的东西.
考虑以下简单程序:
#include <cstring>
#include <cstdio>
#include <cstdlib>
void replace(char *str, size_t len) {
for (size_t i = 0; i < len; i++) {
if (str[i] == '/') {
str[i] = '_';
}
}
}
const char *global_str = "the quick brown fox jumps over the lazy dog";
int main(int argc, char **argv) {
const char *str = argc > 1 ? argv[1] : global_str;
replace(const_cast<char *>(str), std::strlen(str));
puts(str);
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
它在命令行上使用(可选)字符串并打印它,并/替换为字符_.该替换功能由c_repl功能1实现.例如, …
考虑以下情况
// Global
int x = 0; // not atomic
// Thread 1
x = 1;
// Thread 2
if (false)
x = 2;
Run Code Online (Sandbox Code Playgroud)
根据标准,这是否构成数据竞争?[intro.races] 说:
如果两个表达式求值之一修改内存位置 (4.4),而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。
如果程序的执行包含两个潜在并发冲突的操作,并且至少其中一个操作不是原子操作,并且两者都发生在另一个操作之前,则该程序的执行将包含数据争用,除了下面描述的信号处理程序的特殊情况之外。任何此类数据竞争都会导致未定义的行为。
从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值” x = 2;?
从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?
激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:
// Thread 1:
r1 = y.load(std::memory_order_relaxed);
if (r1 == 42) x.store(r1, std::memory_order_relaxed);
// Thread 2:
r2 = x.load(std::memory_order_relaxed);
if (r2 == 42) y.store(42, std::memory_order_relaxed);
// This is allowed to result in r1==r2==42 in c++11
Run Code Online (Sandbox Code Playgroud)
假设我有2个线程:
int value = 0;
std::atomic<bool> ready = false;
thread 1:
value = 1
ready = true;
thread 2:
while (!ready);
std::cout << value;
Run Code Online (Sandbox Code Playgroud)
这个程序能输出0吗?
我读到了有关C ++内存模型的信息-具体来说,是顺序一致性,我认为这是默认设置,但并不是特别清楚。是仅要求编译器相对于彼此以正确的顺序放置原子操作,还是相对于所有其他操作以正确的顺序放置原子操作?
由于今天是美国的感恩节,所以我将被指定为土耳其来问这个问题:
Take something as innocuous as this. An atomic with a simple plain old data type such as an int:
atomic<int> x;
cout << x;
Run Code Online (Sandbox Code Playgroud)
The above will print out garbage (undefined) data. Which makes sense given what I read for the atomic constuctor:
(1) default constructor
Leaves the atomic object in an uninitialized state. An uninitialized atomic object may later be initialized by calling atomic_init.
Feels like an odd committee decision. But I'm sure they had their reasons. …
我知道atomic<T>当多个线程正在读取和写入变量时,将会对类型“T”变量应用锁定,确保只有其中一个线程正在执行读/写操作。
但在多CPU核心计算机中,线程可以运行在不同的核心上,不同的核心会有不同的L1-cache、L2-cache,同时共享L3-cache。我们知道有时C++编译器会优化将变量存储在寄存器中,这样如果变量没有存储在内存中,那么该变量上的不同core-cache之间就没有内存同步。
如果atomic<T>编译器将变量优化为某个寄存器变量,那么它不会存储在内存中,当一个核心写入其值时,另一个核心可能会读出陈旧的值,对吗?这个数据一致性有保证吗?