为了账号安全,请及时绑定邮箱和手机立即绑定

为什么易失性在多线程C或C+编程中不被认为是有用的?

为什么易失性在多线程C或C+编程中不被认为是有用的?

C++ C
米琪卡哇伊 2019-06-12 21:19:30
为什么易失性在多线程C或C+编程中不被认为是有用的?如上文所示这个答案我最近发帖说,我似乎对它的效用(或缺乏)感到困惑。volatile在多线程编程环境中。我的理解是:当一个变量在访问它的代码的控制流之外被更改时,这个变量应该声明为volatile..信号处理程序、I/O寄存器和由另一个线程修改的变量都构成这种情况。所以,如果您有一个全局intfoo,和foo读取线程由一个线程读取并由另一个线程原子地设置(可能使用适当的机器指令),读取线程看到这种情况的方式与它看到由信号处理程序调整的变量或由外部硬件条件修改的变量相同。foo应宣布volatile(或者,对于多线程情况,使用内存隔离负载进行访问,这可能是一个更好的解决方案)。我怎么错了,哪里错了?
查看完整描述

3 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

与.有关的问题volatile在多线程上下文中,它不提供我们需要的保证。它确实有一些我们需要的属性,但并不是所有的属性,所以我们不能依赖它们。volatile 独处.

但是,我们必须使用的原语残存属性还提供了volatile是的,所以实际上是不必要的。

对于共享数据的线程安全访问,我们需要保证:

  • 读/写实际上发生了(编译器将不只是将值存储在寄存器中,并将更新主内存推迟到很久以后)。
  • 不会重新排序。假设我们使用

    volatile

    变量作为标志,指示是否准备读取某些数据。在我们的代码中,我们只是在准备数据之后设置标志,所以所有

    相貌

    很好。但是,如果指令被重新排序,以设置标志呢?

    第一?

volatile确实保证了第一点。它还保证不会发生重排序。在不同的易失性读/写之间..全volatile内存访问将按指定的顺序进行。这就是我们所需要的volatile用于:操作I/O寄存器或内存映射硬件,但它无助于多线程代码中的volatile对象通常仅用于同步对非易失性数据的访问。这些访问仍然可以相对于volatile一张。

防止重新排序的解决方案是使用记忆屏障,它向编译器和CPU指示在这一点上,不能对内存访问进行重新排序。..在易失性变量访问周围设置这样的屏障,可以确保即使是非易失性访问也不会在易失性访问中被重新排序,从而允许我们编写线程安全的代码。

然而,记忆障碍确保在达到障碍时执行所有挂起的读/写,因此它有效地为我们自己提供了我们所需要的一切,volatile没有必要。我们可以移除volatile完全是限定符。

从C+11开始,原子变量(std::atomic<T>)给我们所有相关的保证。


查看完整回答
反对 回复 2019-06-12
?
眼眸繁星

TA贡献1873条经验 获得超9个赞

我不认为你错了-要保证线程A会看到值的变化,就必须保证,如果值是由线程A以外的东西改变的,我理解它基本上是一种告诉编译器“不要将这个变量缓存在寄存器中,而是确保始终从每次访问的RAM内存中读取/写入它”。

混淆是因为易失性不足以实现许多事情。特别是,现代系统使用多级缓存,现代多核CPU在运行时进行一些奇特的优化,现代编译器在编译时进行一些奇特的优化,这些都会导致各种副作用的出现,其顺序与您仅仅查看源代码时所期望的顺序不同。

所以不稳定是很好的,只要你记住,挥发性变量的“观察”变化可能不会发生在你认为会发生的确切时间。特别是,不要尝试使用易失性变量作为跨线程同步或排序操作的方式,因为它不能可靠地工作。

就我个人而言,我的主菜(只有?)用于易失性标志是一个“joeGoAwayNow”布尔值。如果我有一个连续循环的工作线程,我将让它检查循环的每一次迭代中的易失性布尔值,如果布尔值为真,则退出。然后,主线程可以安全地清除工作线程,方法是将布尔值设置为true,然后调用p线程_join()等待工作线程消失。


查看完整回答
反对 回复 2019-06-12
  • 3 回答
  • 0 关注
  • 463 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信