【发布时间】:2021-12-26 23:54:26
【问题描述】:
编译器 (gcc) 只是假设静态变量永远不会被其他线程触及,即使优化级别最低。我试图读取从另一个线程写入的值,但 gcc 只是认为该值从未改变。是否读取由另一个线程未定义行为修改的静态变量的值?
我特别询问编译器所做的假设。不是关于当程序没有正确处理线程同步时会发生什么。
为了向未来的读者澄清,只有选定的答案才能清楚地回答我在标题中写的问题。它并没有解决我遇到的实际问题,但这就是我所问的。不过,我想澄清一下实际问题是什么,以及我是如何最终理解编译器在做什么的。
给定一个静态全局变量n,
static int n;
我将n 放入一个循环中以制造一个错误的自旋锁。
while (!n); doSth();
除非n 是volatile 或_Atomic,否则编译器将简单地假设n 的值不会在循环内改变。
然后我注意到依赖于信号处理程序的代码部分按预期工作。
n = 0; //added for explanation
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGUSR1);
sigwait(&s, (int *)&_);
if (n) doSth(); //the compiler still checks the value of `n`
我一开始以为sigwait 有什么特别之处,但事实并非如此。有了这个更简单的例子,
n = 0;
putchar(0);
if (n) doSth();
编译器仍然不能假定n 的值是0,因为putchar 修改n 的值可能会产生副作用,因为n 是一个全局变量。
当然,任何理智的编译器都会对此进行优化。
n = 0;
if (n) doSth();
毕竟,有了一个不错的信号处理程序,一切都可以正常工作。
【问题讨论】:
-
@sj95216 这仅适用于“两者都不会在另一个之前发生”的数据竞争条件。如果从另一个线程修改后读取清楚了值怎么办?
-
一般情况下,如果变量不是
volatile,不是原子的,也不是被内存屏障保护的,优化器会像单线程一样优化对它的访问。 -
在大多数情况下,它甚至不是编译器的假设,而是 cpu 本身的假设,因为内存中的值是通过多级缓存缓冲区缓存的。编译器关心的只是volatile,它不能替代原子操作等。
-
我不认为静态变量的规则(re:多线程)与一般变量的规则有什么不同。
-
@xiver77 "happens before" 没有通俗的意思。它是 5.1.2.4.18 中定义的技术术语。特别是“发生在之前”是指在同一线程中之前排序或通过原子/互斥体/栅栏以某种方式同步。
标签: c multithreading language-lawyer undefined-behavior