C / C ++的基本types是primefaces吗?
C / C ++的基本types,如int
, double
等,primefaces,如线程安全吗?
他们是否从数据竞赛中解脱出来? 也就是说,如果一个线程写入一个这样的types的对象,而另一个线程从中读取,则行为是否定义良好?
如果没有,是否依赖于编译器或其他东西?
不,基本的数据types(例如, int
, double
)不是primefaces的,参见std::atomic
。
相反,你可以使用std::atomic<int>
或者std::atomic<double>
。
注意:在C ++ 11中引入了std::atomic
,我的理解是,在C ++ 11之前,C ++标准根本不能识别multithreading的存在。
正如@Josh所指出的, std::atomic_flag
是一个primefaces布尔types。 它保证是无锁的 ,不像std::atomic
特殊化。
引用的文档来自: http : //open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf 。 我很确定标准不是免费的,因此这不是最终的/官方的版本。
1.10multithreading执行和数据竞赛
- 如果其中一个修改一个内存位置(1.7),而另一个读取或修改相同的内存位置,那么两个expression式评估会发生冲突。
- 该库定义了一些特殊标识为同步操作的primefaces操作(第29章)和互斥操作(第30章)。 这些操作在使一个线程中的分配对另一个线程可见时发挥着特殊的作用。 在一个或多个存储器位置上的同步操作是消费操作,获取操作,释放操作,或者获取和释放操作。 没有关联的内存位置的同步操作是一个围栏,可以是一个获取围栏,一个释放围栏,或者一个获取和释放围栏。 另外,还有宽松的primefaces操作,它们不是同步操作,还有primefaces读 – 修改 – 写操作,它们有特殊的特性。
- 两个动作可能是并发的
(23.1) – 它们由不同的线程执行,或者
(23.2) – 它们是无序的,至less有一个是由信号处理程序执行的。
如果程序包含两个可能并发的冲突动作,其中至less有一个不是primefaces的,并且两个都不会发生在另一个之前,那么程序的执行包含数据争用,除了下面介绍的信号处理程序的特殊情况。 任何这样的数据竞争都会导致未定义的行为。
29.5primefacestypes
- 对于整型typeschar,
signed char
,unsigned char
,short
,unsigned short
,int
,unsigned int
,long
,unsigned long
,long long
,unsigned long long
,char16_
,char32_t
,wchar_t
,以及头文件<cstdint>
所需的其他任何types。 对于每个整型types的整数,专门化atomic<integral>
提供了适合于整型types的附加primefaces操作。 应该有一个专门的atomic<bool>
,它提供了29.6.1中规定的一般primefaces操作。
- 应该有primefaces类模板的指针部分特化。 这些专业化应该有标准的布局,简单的默认构造函数和简单的析构函数。 它们都应该支持聚合初始化语法。
29.7标志types和操作
- 对atomic_flagtypes的对象的操作应该是无锁的。 [注:因此操作也应该是无地址的。 没有其他types需要无锁操作,所以atomic_flagtypes是符合此国际标准所需的最低硬件实现types。 其余的types可以用atomic_flag来模拟,尽pipe不够理想的属性。 – 结束注意]
由于C也是(当前)在问题中提到的,尽pipe不在标签中, C标准指出:
5.1.2.3程序执行
…
当抽象机器的处理由于接收信号而中断时,既不是无lockingprimefaces对象,也不是types
volatile sig_atomic_t
对象的volatile sig_atomic_t
是未指定的,浮点环境的状态也是如此。 当处理程序退出时,由处理程序修改的既不是无锁primefaces对象也不是types为volatile sig_atomic_t
的任何对象的值都变得不确定,就像处理程序修改并且未恢复的浮点环境的状态一样到原来的状态。
和
5.1.2.4multithreading执行和数据竞争
…
如果其中一个修改一个内存位置,另一个读取或修改相同的内存位置,那么两个expression式评估会发生冲突 。
[几页标准 – 一些段落明确地处理primefacestypes]
如果一个程序的执行包含一个数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至less有一个不是primefaces的,而且两个都不会发生在另一个线程之前。 任何这样的数据竞争都会导致未定义的行为。
请注意,如果信号中断处理,则值是“不确定的”,并且对不是明确primefaces的types的同时访问是未定义的行为。
什么是primefaces?
primefaces,用一个primefaces的性质来描述。 primefaces这个词源自拉丁语“primefaces”,意思是“不可分割的”。
通常我会想到一个primefaces操作(不pipe语言)有两个特质:
primefaces操作总是不分割的。
即它是以不可分割的方式进行的,我相信这就是OP所说的“线程安全”。 从某种意义上说,当另一个线程查看时,该操作瞬间发生。
例如下面的操作可能被分割(依赖于编译器/硬件):
i += 1;
因为它可以被另一个线程(假设的硬件和编译器)观察为:
load r1, i; addi r1, #1; store i, r1;
两个线程在没有适当同步的情况下执行上述操作i += 1
可能会产生错误的结果。 先说i=0
,线程T1
加载T1.r1 = 0
,线程T2
加载t2.r1 = 0
。 两个线程将它们各自的r1
加1,然后将结果存储到i
。 尽pipe已经执行了两次递增,但由于递增操作是可分的,所以i
的值仍然只有1。 请注意,如果在i+=1
之前和之后有同步,则另一个线程将一直等待,直到操作完成,从而观察到一个不可分割的操作。
请注意,即使是一个简单的写可能会或可能不会被分开:
i = 3; store i, #3;
取决于编译器和硬件。 例如,如果i
的地址没有适当地alignment,则必须使用由CPU执行的未alignment的加载/存储,作为多个较小的加载/存储。
primefaces操作保证了内存sorting语义。
非primefaces操作可能被重新sorting,可能不一定以程序源代码中写入的顺序出现。
例如,在“as-if”规则下,只要所有对易失性存储器的访问按程序指定的顺序进行,“编译器”就被允许重新sorting存储和加载。根据标准的措词。 因此,非primefaces操作可以被重新排列,以破坏关于multithreading程序中的执行顺序的任何假设。 这就是为什么在multithreading编程中看似天真地使用原始int
作为信号variables被破坏,即使写入和读取可能是不可分割的,sorting可能会根据编译器而中断程序。 primefaces操作根据指定的内存语义强制执行其周围操作的sorting。 见std::memory_order
。
CPU也可以在该CPU的内存sorting约束下重新sorting内存访问。 您可以从第2212页开始,在Intel 64和IA32体系结构软件开发人员手册第8.2节中findx86体系结构的内存sorting约束条件。
原始types( int
, char
等)不是primefaces的
因为即使它们在某些条件下可能有不可分割的存储和加载指令,甚至可能有一些算术指令,但它们并不保证存储和加载的顺序。 因此,在没有正确同步的情况下,在multithreading上下文中使用它们是不安全的,以保证其他线程观察到的内存状态是您认为在那个时间点上的内存状态。
我希望这解释了为什么原始types不是primefaces的。
到目前为止还没有在其他答案中提到的其他信息:
例如,如果使用std::atomic<bool>
,并且bool
实际上是目标架构上的primefaces,则编译器将不会生成任何冗余的栅栏或锁。 相同的代码将生成为一个普通的bool
。
换句话说,使用std::atomic
只会使代码的效率降低,如果实际上需要在平台上进行正确的处理。 所以没有理由避免它。