并发性:C ++ 11内存模型中的primefaces和易失性
一个全局variables在两个不同核心上的两个并发运行的线程之间共享。 线程写入和读取variables。 对于primefacesvariables,一个线程可以读取一个陈旧的值? 每个内核可能在其caching中具有共享variables的值,并且当一个线程写入其caching中的副本时,另一个内核中的另一个线程可能会从自己的caching中读取过时值。 或者编译器执行强大的内存sorting来从其他caching中读取最新值? c ++ 11标准库有std :: atomic支持。 这与volatile关键字有什么不同? 在上述情况下,volatile和atomictypes的performance会有什么不同?
首先, volatile
不意味着primefaces访问。 它是专为内存映射I / O和信号处理的东西而devise的。 当与std::atomic
一起使用时, volatile
是完全不必要的,除非你的平台另有文档, volatile
对于线程之间的primefaces访问或内存sorting没有任何影响。
如果你有一个在线程之间共享的全局variables,比如:
std::atomic<int> ai;
那么可见性和sorting约束依赖于用于操作的内存sorting参数,以及锁,线程和对其他primefacesvariables的访问的同步效果。
在没有任何额外的同步的情况下,如果一个线程向ai
写入一个值,那么没有任何东西可以保证另一个线程在任何给定的时间段内都能看到这个值。 该标准规定它应该在“合理的时间段内”可见,但是任何给定的访问可能会返回陈旧的值。
std::memory_order_seq_cst
的缺省内存顺序为所有variables的所有std::memory_order_seq_cst
操作提供了单个全局总顺序。 这并不意味着你不能得到陈旧的价值观,但这确实意味着你所得到的价值决定了你的操作在这个总的顺序中的位置。
如果你有2个共享variablesx
和y
,最初为零,并且有一个线程写入1到x
,另一个写入2到y
,那么第三个读取这两个线程的线程可能会看到(0,0),(1,0), (0,2)或(1,2),因为操作之间不存在sorting约束,因此操作可能以全局顺序以任何顺序出现。
如果两个写入来自同一个线程,在y=2
之前x=1
,读取线程在x
之前读取y
,则(0,2)不再是有效的选项,因为y==2
的读取意味着先前写入x
是可见的。 其他3个配对(0,0),(1,0)和(1,2)仍然是可能的,这取决于2个读取如何与2个写入交错。
如果您使用其他内存sorting,如std::memory_order_relaxed
或std::memory_order_acquire
那么约束将进一步放宽,并且单个全局sorting不再适用。 如果没有额外的同步,线程甚至不一定要在两个存储区的顺序上分开variables。
唯一可以保证您具有“最新”值的方法是使用读取 – 修改 – 写入操作,例如exchange()
, compare_exchange_strong()
或fetch_add()
。 读取 – 修改 – 写入操作有一个额外的约束,即它们总是以“最新”值运行,所以一系列线程的ai.fetch_add(1)
操作序列将返回一系列没有重复或间隙的值。 在没有附加约束的情况下,仍然不能保证哪个线程将会看到哪些值。
处理primefaces操作是一个复杂的话题。 我build议你阅读大量的背景材料,并在用primefaces编写生产代码之前检查已发布的代码。 在大多数情况下,编写使用锁的代码比较容易,效率也不明显。
volatile
和primefaces操作有不同的背景,并介绍了不同的意图。
从返回的volatile
date,主要是为了防止编译器优化访问内存映射IO时。 现代编译器往往只会抑制volatile
优化,尽pipe在某些机器上,即使是内存映射IO也是不够的。 除了信号处理程序的特殊情况,以及setjmp
, longjmp
和getjmp
序列(在C标准和信号的情况下,Posix标准提供了额外的保证)之外,在现代机器上必须被认为是无用的,特殊的附加指令(栅栏或内存屏障),硬件可能会重新sorting甚至抑制某些访问。 既然你不应该使用setjmp
et al。 在C ++中,这或多或less会留下信号处理程序,而在multithreading环境中,至less在Unix下,这些方法也有更好的解决scheme。 如果您正在处理核心代码,并且可以确保编译器生成相关平台所需的任何内容,则可能还会映射内存映射IO。 (根据标准, volatile
访问是编译器必须遵守的可观察行为,但是编译器可以定义“访问”的含义,大多数似乎将其定义为“执行加载或存储机器指令”。在现代处理器上,这甚至不意味着在总线上必然有读写周期,更不用说按照您期望的顺序。)
鉴于这种情况,C ++标准增加了primefaces访问,它确实提供了跨线程的一定数量的保证。 特别是围绕primefaces访问生成的代码将包含必要的附加指令,以防止硬件重新sorting访问,并确保访问传播到多核计算机上的核心间共享的全局内存。 (在标准化工作中,微软曾经build议将这些语义join到volatile
,我认为他们的一些C ++编译器是这样做的,但是在讨论了委员会中的问题之后,包括微软代表在内的普遍共识就是最好是保留volatile
的原始含义,并定义primefacestypes)。或者只是使用系统级原语,如互斥体,它们执行代码中需要的任何指令。 (他们不得不在没有关于内存访问顺序的保证的情况下实现一个互斥锁。)
挥发性和primefaces性服务于不同的目的。
易失性:通知编译器避免优化。 该关键字用于意外更改的variables。 因此,它可以用来表示multithreading应用程序中共享的硬件状态寄存器,ISRvariables,variables。
primefaces:它也用于multithreading应用的情况。 但是,这确保了在multithreading应用程序中使用时没有locking/失速。 primefaces行动是没有种族和不可分割的。 使用的关键场景很less是在multithreading应用程序中检查锁是否空闲或使用,自动添加值并返回附加值等。
以下是两件事情的基本概要:
1)挥发性关键字:
告诉编译器这个值可能随时改变,因此它不应该caching在寄存器中。 在C中查找旧的“注册”关键字。“易失性”基本上是“ – ”运算符,用于“注册”的“+”。 现代编译器现在进行优化,默认情况下“注册”用于明确请求,所以你只能看到“易失性”。 使用volatile限定符将保证您的处理从不使用陈旧的值,但仅此而已。
2)primefaces:
primefaces操作修改数据在一个单一的时钟滴答,所以任何其他线程不可能在这样的更新中访问数据。 它们通常限于硬件支持的任何单时钟汇编指令; 比如++, – 和交换2个指针。 请注意,这没有提及ORDER,不同的线程将运行primefaces指令,只是它们永远不会并行运行。 这就是为什么你有所有这些额外的选项来强制sorting。