简体   繁体   English

在这种情况下,我可以不使用释放和获得障碍吗?

[英]Can I not use release and acquire barriers in this case?

This tutorial says the following: 本教程说明以下内容:

every load on x86/64 already implies acquire semantics and every store implies release semantics. x86 / 64上的每个负载都已经隐含了获取语义,每个商店都隐含了释放语义。

Now say I have the following code (I wrote my questions in the comments): 现在说我有以下代码(我在注释中写了问题):

/* Global Variables */

int flag = 0;
int number1;
int number2;

//------------------------------------

/* Thread A */

number1 = 12345;
number2 = 678910;
flag = 1; /* This is a "store", so can I not use a release barrier here? */

//------------------------------------

/* Thread B */

while (flag == 0) {} /* This is a "load", so can I not use an acquire barrier here? */
printf("%d", number1);
printf("%d", number2);

The tutorial is talking about loads and stores at the assembly/machine level, where you can rely on the x86 ISA semantics which include acquire-on-load and release-on-store. 本教程讨论的是程序集/机器级别的加载存储 ,您可以在其中依赖x86 ISA语义,包括加载时获取和存储中释放。

Your code, however, is C which provides no such guarantees at all, and the compiler is free to transform it to something entirely different than what you'd expect in terms of loads and stores. 但是,您的代码是C ,它根本不提供任何此类保证,并且编译器可以自由地将其转换为与加载和存储所期望的完全不同的东西。 This isn't theoretical, it happens in practice. 这不是理论上的,而是在实践中发生的。 So the short answer is: no, it's not possible to do that portably, legally in C - although it might work if you get lucky. 因此,简短的答案是:不,不可能合法地在C语言中进行可移植的操作-尽管如果幸运的话,它可能会起作用。

Let's assume that the code is written in C99 and the target architecture is x86. 假设代码是用C99编写的,目标体系结构是x86。 The strong memory model of x86 takes effect only at the machine code level. x86的强存储模型仅在机器代码级别有效。 C99 doesn't have a memory model. C99没有内存模型。 I'll explain what can go wrong and discuss whether there is a C99-compliant way of handling the issues. 我将解释可能出问题的地方,并讨论是否存在符合C99的问题处理方式。

First, we have to make sure that none of the variables get optimized away and that all accesses to flag , number1 , and number2 occur from memory rather than cached in CPU registers 1 . 首先,我们必须确保所有变量都没有被优化,并且对flagnumber1number2所有访问都发生在内存中,而不是缓存在CPU寄存器1中 This can be achieved in C99 by qualifying all of the three variables with volatile . 这可以在C99中通过使用volatile限定所有三个变量来实现。

Second, we have to ensure that the store to flag in the first thread has release semantics. 其次,我们必须确保第一个线程中要flag的存储具有释放语义。 These semantics include two guarantees: the store to flag does not get reordered with previous memory accesses and making the store visible to the second thread. 这些语义包括两个保证: flag的存储不会与先前的内存访问一起重新排序,并使存储对第二个线程可见。 The volatile keyword tells the compiler that accesses to the variable may have observable side effects. volatile关键字告诉编译器对变量的访问可能会有明显的副作用。 This prevents the compiler from reordering accesses to volatile variables with respect to other operations that are also considered to have observable side effects by the compiler. 这样可以防止编译器针对其他操作重新排序对易失性变量的访问,其他操作也被编译器视为具有可观察到的副作用。 That is, by making all of the three variables volatile , the compiler will maintain the order of all the three stores in the first thread. 也就是说,通过使所有三个变量均为volatile ,编译器将在第一个线程中维护所有三个存储的顺序。 That said, if there are other non-volatile memory accesses that are above or below the store to flag , then such accesses can still be reordered. 也就是说,如果在存储之上或之下有其他非易失性存储器访问要flag ,则仍可以对这些访问进行重新排序。 So the standard volatile provides only partial release semantics. 因此,标准volatile仅提供部分发布语义。

Third... actually for your particular piece of code, atomicity is not required. 第三...实际上对于您的特定代码段,不需要原子性。 That's because the store to flag only changes one bit, which is inherently atomic. 这是因为要flag的存储区仅更改一位,这本质上是原子的。 So for this particular code, you don't have to worry about atomicity. 因此,对于此特定代码,您不必担心原子性。 But in general, if the store to flag may change more than one bit and if the condition checked in the second thread may behave differently depending on whether it sees all or some of the bit changes, then you'd certainly need to ensure that accesses to 'flag` are atomic. 但是通常,如果存储到flag可能改变多个位,并且如果第二个线程中检查的条件取决于看到的是全部还是部分位改变,则行为可能有所不同,那么您肯定需要确保访问到'flag'是原子的。 Unfortunately, C99 has no notion of atomicity. 不幸的是,C99没有原子性的概念。

To get full release semantics and atomicity, you can either use C11 atomics (as discussed in the article you cited) or you can resort to compiler-specific techniques (also discussed in the article you cited). 要获得完整的发布语义和原子性,您可以使用C11原子(如您引用的文章中所述),也可以诉诸于编译器特定的技术(也如您所引用的文章中所述)。 Of course, you can still just look at the generated machine code and see whether the x86 memory model itself offers the necessary requirements for correctness. 当然,您仍然可以只查看生成的机器代码,并查看x86内存模型本身是否提供了正确性的必要要求。 This is not feasible on large code bases. 在大型代码库上这是不可行的。 In addition, the next time the code is compiled, the generated machine code may change. 另外,下次编译代码时,生成的机器代码可能会更改。 Finally, since you're merely a human, you may make a mistake. 最后,由于您只是人,所以您可能会犯错。


(1) In the cited article, the variable A is declared as a shared global variable. (1)在引用的文章中,变量A被声明为共享全局变量。 Now most probably the compiler will allocate it from memory. 现在,编译器很可能将从内存中分配它。 But is this strictly standard compliant? 但这是否严格符合标准? What prevents the compiler from allocating it in a register for the whole lifetime of the program? 是什么阻止编译器在程序的整个生命周期内将其分配到寄存器中? Not sure about that. 不确定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM