用于信号处理程序和多线程的 volatile

volatile for signal handler and multi-threading

据说信号处理程序需要volatile,例如

volatile int flag = 1; // volatile is needed here?

void run() {
    while(flag) { /* do someting... */ }
}

void signal_handler(int sig) {
    flag = 0;
}

int main() {
    signal(SIGINT, sig_handler);
    run();
    // ...
}

据说volatile在多线程中经常用不到。但是在多线程中类似上面的情况怎么样:

int flag = 1; // is volatile needed here?

void thread_function() {
    while(flag) { /* do someting... */ }
}

int main() {
    // pthread_create() to create thread_function()...
    sleep(10); // let thread_function run for 10 seconds
    flag = 0;
    // ...
}

是否应该在两种情况下都使用 volatile 关键字?编译器对这两种情况的处理方式相同吗?

唯一允许从信号处理程序修改的 non-local 值是类型 volatile sig_atomic_t 和原子类型的值。特别是,写入您的 volatile int 不允许的 ,并且如果您的信号处理程序运行,则您有未定义的行为。

volatile 用于确保变量的内容是从其实际位置(在我们的例子中是内存)而不是从 CPU 寄存器中读取的。

换句话说,每当“外部”事件可能会更改变量的值时,您应该考虑使用 volatile(“外部”- 在相关代码块之外)。

在您的两个示例中,您都使用变量作为标志来表示行为发生变化。在这两个示例中,该标志都由检查该标志的循环“外部”的事件控制。因此,两个示例都需要使用 volatile 关键字。

应该注意 volatile 提供线程安全的原因有很多。为确保对象是线程安全的,read/write 操作必须是受保护的或原子的。

C++ 标准,在 [intro.execution],第 6 段,告诉:

When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither
— of type volatile std::sig_atomic_t nor
— lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.

因此,是的,对于信号处理程序,您必须使用 volatile std::sig_atomic_t