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

Dan*_*n Z 6 c performance multithreading compiler-optimization

我有关于编译器改变执行顺序的问题.我试图通过用信号机制(thorugh信号量)替换临界区来提高多线程程序(C语言)的性能.

我需要保证这里的执行顺序,并且已经对此进行了一些研究.我在函数中看到了许多关于执行顺序的问题,但对函数中的函数没有太多讨论.

基于https://en.wikipedia.org/wiki/Sequence_point规则#4,下面的代码块会保证*p->afunc2输入之前必须首先进行评估,因为func2p作为输入(假设编译器遵守调度点的规则)在这里定义)?

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

func2 (struct *p) {
  p->b = y;
  releaseSemaphore(s);
}
Run Code Online (Sandbox Code Playgroud)

至关重要的p->b是,只有在p->a设置为另一个线程处于处理各种请求的循环中并且通过是否p->b设置来识别有效请求时才设置.释放信号量只会在空闲(并等待信号量)时触发任务,但如果它忙于处理其他请求,它将在稍后检查p->b,并且我们不能保证func1仅在该线程空闲时调用.

Com*_*sMS 2

不会。序列点排序不会越过线程边界进行转换。这就是为什么我们首先需要内存排序保证的全部原因。

对于执行代码的线程,序列点排序始终得到保证(模数 as-if-rule)。任何其他线程都可以以任意顺序观察该线程的写入。这意味着即使线程 #1 可以验证它是否按特定顺序执行写入,线程 #2 仍可能以不同的顺序观察它们。这就是为什么 volatility 在这里也是不够的。

从技术上讲,这可以解释,例如。通过缓存。线程 #1 的写入可能会首先进入写入缓冲区,但线程 #2 仍然看不到它们。只有当写缓冲区被刷新回主内存时,它们才变得可见,并且允许硬件在刷新之前对写入进行重新排序。

请注意,仅仅因为平台允许重新排序写入并不意味着它会这样做。这是危险的部分。在一个平台上运行得很好的代码在移植到另一个平台时可能会突然崩溃。使用正确的内存顺序可以保证代码在任何地方都能工作。