【问题标题】:C++98: is volatile needed for access to shared global objects in a multithreaded applicationC++98:在多线程应用程序中访问共享全局对象需要 volatile
【发布时间】:2020-11-14 00:20:20
【问题描述】:

遗憾的是,我被 C++98 卡住了,我在嵌入式应用程序中使用它。

我的问题是:我有一个多线程应用程序,具有各种全局共享变量(邪恶,我知道)。 我确实使用互斥锁保护对它们的每次访问。我是否还需要将这些全局变量声明为 volatile,以防止编译器优化对它们的访问?

网上搜了一下,好像volatile对于多线程是绝对没用的,但是很多文章都是和C++11相关的,里面确实介绍了一个识别线程的内存模型,但是我是在C++98的地盘。 我还发现了一些资源表明 volatile 在我的情况下反而有用,例如 this Barr Group's article

让我强调一个事实,即我根本不想摆脱互斥锁,或者尝试无锁编程。互斥锁绝对保留,我只是想了解是否需要 volatile 关键字。

【问题讨论】:

  • 使用互斥锁。 volatile 不能保护您免受竞争条件的影响
  • @bobra 我对他们问题的解读是他们理解这一点。
  • 我担心的不是竞态条件,而是编译器优化。
  • stackoverflow.com/questions/50951011/… 表示互斥锁可以保护您免受 cpu 重新排序,而stackoverflow.com/questions/11172922/… 弱建议您可以避免编译器重新排序。
  • @fraben 编译器并没有你想象的那么糟糕:D volatile 强制你的程序在内存中读/写这个变量,而不是使用 cpu 缓存。

标签: multithreading volatile c++98


【解决方案1】:

我是否还需要将这些全局变量声明为 volatile,以防止编译器优化对它们的访问?

没有。如果你这样做了,你仍然会遇到麻烦,因为volatile 是不够的——编译器以外的东西(例如 CPU、发布缓冲区和内存控制器)也可以优化访问。

我相信您已经在其他地方读到过,volatile 在 C++98 中没有定义的多线程语义。因此,除非它在您的特定线程标准中使用(您没有指定),否则它对您完全没用。

您的代码大概正确地使用了互斥锁。不允许任何优化破坏仅依赖于相关标准或实现提供的保证的代码。因此,如果您正确使用互斥锁,那么您的代码就可以保证工作。

【讨论】:

  • 感谢您的回答 :) 我真的不希望 volatile 有任何特定的多线程语义。让我担心的是,互斥锁与它所保护的变量没有任何关系,因此编译器可能会尝试优化。例如,我在betterembsw.blogspot.com/2014/06/… 读到了这篇文章。在cmets部分,帖子的作者基本都说了我刚刚写的。
  • @fraben 你担心错了。编译器不是系统中唯一允许优化代码的东西。系统中的一切都可以优化代码,包括CPU、内存控制器等。保护自己的最好方法是正确使用互斥锁。不允许任何东西破坏正确的代码。除非你的线程库文档说 volatile 对线程做了特别的事情,否则期望它做任何特别的事情是一个巨大的错误。
【解决方案2】:

关键字volatile是什么意思?

您强制您的程序始终将您的变量读/写到内存中(例如每次缓存未命中)。总是(CPU->L1->L2->L3->Bus->MemoryMemory->Bus->L3->L2->L1->CPU) 实际上它会减慢你的程序,所以你不应该在确切需要之前使用它。

您可能知道编译器可能会进行一些优化,但这些优化不会影响/更改您的程序逻辑。

示例 1:

int d = 5; 
int b = 10;
for (int i=0; i < 1e9; i++){
    cout << i;
    b++;
}
cout << d; // d may be created only here, or not created at all. compiler may just cout << 5;
      // var b - seems to be skipped at all due to being unused

示例 2:

int a = 0;
for (int i = 0; i < 10; i++){
    a++;
    sleep(1000);
}
cout << a;

// this code could be compiled like the following one
sleep(10000); 
cout << 10;

关键字volatile 阻止您的var 进行此类优化。 即 vars a, b 将被创建并递增,缓存也总是丢失。

这是使用volatile 的最常见用例之一:

您有一个附加到 var 地址的第三方传感器或记分牌,它的值始终被读取并显示在记分牌上。所以在这种情况下,您需要volatile,以防止对其进行编译器优化,以便您的计分板可以显示正确的值。

我想现在你可以决定是否需要volatile

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多