简体   繁体   English

在不使用volatile或内存屏障和锁定的情况下保证执行顺序

[英]Guaranteeing the order of execution without using volatile or memory barrier and locks

I have Question regarding on compiler changing the order of execution. 我有关于编译器改变执行顺序的问题。 I am trying to improve performance of a multi-thread program (C language) by replacing the critical section with a signaling mechanism (thorugh semaphore). 我试图通过用信号机制(thorugh信号量)替换临界区来提高多线程程序(C语言)的性能。

I need to guarantee the order of execution here, and have been doing some research on this. 我需要保证这里的执行顺序,并且已经对此进行了一些研究。 I saw many questions on the order of execution within a function, but not much discussion on a function within a function. 我在函数中看到了许多关于执行顺序的问题,但对函数中的函数没有太多讨论。

Based on https://en.wikipedia.org/wiki/Sequence_point rules #4 , would the below code chunk guarantees that *p->a has to be evaluated first before func2 is entered since func2 takes p as an input (assuming the compiler adheres to the rules of schedule point defined here)? 基于https://en.wikipedia.org/wiki/Sequence_point规则#4,将下面的代码块保证*p->a必须首先评估之前func2输入自func2采用p作为输入(假设编译器遵守此处定义的调度点规则)?

func1 (struct *p) {
  p->a = x;  
  func2 (p);
}

func2 (struct *p) {
  p->b = y;
  releaseSemaphore(s);
}

It is critical that p->b is set only after p->a is set as another thread is in a loop processing various request and identifies a valid request by whether p->b is set. 至关重要的是, p->b仅在p->a被设置之后才被设置为另一个线程在处理各种请求的循环中并且通过是否设置p->b来识别有效请求。 Releasing semaphore only triggers the task if it is idle (and waiting for the semaphore), but if it is busy processing other requests, it will check p->b later, and we cannot guarantee that func1 is called only when that thread is idle. 释放信号量只会在空闲(并等待信号量)时触发任务,但如果它忙于处理其他请求,它将在稍后检查p->b ,并且我们不能保证仅在该线程空闲时调用func1

No. Sequence point ordering does not transition over thread boundaries. 否。序列点排序不会在线程边界上转换。 That is the whole point of why we need memory ordering guarantees in the first place. 这就是为什么我们首先需要内存排序保证的原因。

The sequence point ordering is always guaranteed (modulo as-if-rule) for the thread which executes the code. 对于执行代码的线程,始终保证序列点排序(模数为if-if-rule)。 Any other thread might observe the writes of that thread in an arbitrary order. 任何其他线程都可能以任意顺序观察该线程的写入。 This means that even if Thread #1 can verify that it performs writes in a certain order, Thread #2 might still observe them in a different order. 这意味着即使线程#1可以验证它以特定顺序执行写入,线程#2仍可能以不同的顺序观察它们。 That is why volatile is also not enough here. 这就是为什么挥发性在这里还不够。

Technically this can be explained eg. 从技术上讲,这可以解释为例如。 by caches. 通过缓存。 The writes by Thread #1 might go to a write buffer first, where they will still be invisible to Thread #2. 线程#1的写入可能首先进入写缓冲区,它们仍然对线程#2不可见。 Only once the write buffer is flushed back to main memory they become visible and the hardware is allowed to reorder the writes before flushing. 只有将写入缓冲区刷回主存储器后,它们才会变为可见,并且允许硬件在刷新之前对写入进行重新排序。

Note that just because the platform is allowed to reorder writes does not mean that it will. 请注意,仅仅因为允许平台重新排序写入并不意味着它会。 This is the dangerous part. 这是危险的部分。 Code that will run perfectly fine on one platform might break out of the blue when being ported to another. 在一个平台上运行完美的代码在移植到另一个平台时可能会突然出现。 Using proper memory orderings guarantees that the code will work everywhere . 使用适当的内存顺序可确保代码随处可用

Implementation can 1 change the ordering as long as this isn't not done over function calls from other translation units. 实施可以1改变顺序,只要这不是没有结束函数完成从其他翻译单元调用。

Such reordering is orthogonal to multithreading, ie it is done in both singlethreaded and multithreaded programs. 这种重新排序与多线程正交,即它在单线程和多线程程序中完成。

If function func2 is in the same translation unit as func1, the execution could be done as if: 如果函数func2与func1在同一个转换单元中,则可以执行以下操作:

func1 (struct *p) 
{
    func2 (p);
    p->a = x;  
}

Use volatile iff you want to prevent 2 such reorderings. 如果你想要防止2次这样的重新排序,请使用volatile。 (Note that this is done to prevent reordering mentioned above, not for other synchronization purposes. You will have to use atomic primitives for those.) (请注意,这样做是为了防止上面提到的重新排序,而不是出于其他同步目的。您将不得不使用原子基元。)


1 (Quoted from: ISO/IEC 9899:201x 5.1.2.3 Program execution 10) 1 (引用自:ISO / IEC 9899:201x 5.1.2.3程序执行10)
Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. 或者,实现可以在每个转换单元内执行各种优化,使得实际语义仅在跨转换单元边界进行函数调用时才符合抽象语义。

2 (Quoted from: ISO/IEC 9899:201x 6.7.3 Type qualifiers 7) 2 (引用自:ISO / IEC 9899:201x 6.7.3类型限定符7)
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. 具有volatile限定类型的对象可能以实现未知的方式进行修改,或者具有其他未知的副作用。 Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. 因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述。 Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. 此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改。

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

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