0

只有我这么想吗?[一秒钟的真正问题。]

除了关于它的巨大混乱,以及它与互斥锁或其他锁定机制的混合,当我处理线程安全情况时,我总是不得不放弃任何使用volatile,因为它根本没有做任何有用的事情。

volatile“禁止对读/写进行任何重新排序或缓存”,但这并没有多大用处,只要volatile 标记一个对象(并污染它,因为它不再是“正常”对象)。

考虑一下:

Thread A            Thread B
reads vars
                    locks mutex (gets access)
locks mutex (waits)
                    writes some vars
                    releases mutex
reads vars again
releases mutex

现在,编译器可能想要优化两个线程 A 的读取,将一些结果保存在寄存器中。你说我应该将这些变量声明为volatile. 我说我不想将所有内容都标记为volatile,因为volatile它是透明的,我必须复制 50% 的代码来支持volatile类型。你() 说锁定互斥锁(至少是 POSIX 互斥锁)要么是编译器可以识别并正确管理的东西,要么是对可以改变世界的库(编译器无法访问)的调用,因此编译器不会假设在这样的电话之后的任何事情。我说这太依赖于实现,非常低级的东西(我不想浏览开发文档进行日常编程)。更糟糕的是,如果由于某种原因“外部库”,出于任何合法原因,编译器可以访问,它可能会突然改变(也许作者将函数转换为必须包含在头文件中的模板......无论如何) .

因此,在我看来,volatile这是完全没用的(并且具有误导性),除非对于一些非常低级的东西(可能是设备读取,但我在这个领域不胜任)。

更好的是用其他方式明确告诉编译器它必须放弃关于任何变量的任何假设,并且每次后续读取都必须是从内存中真正读取。但我想不出有什么比调用一个虚拟外部函数更好的了,这会产生我之前概述的相同问题。有没有一种优雅的方式来做到这一点?

4

3 回答 3

8

我使用低级的东西,这是一个有用的例子。

volatile uint32_t *foo = <<<a register on a pci device>>> 

有可能在foo多次阅读时,我会得到不同的结果。这是因为外部源(pci 设备)正在修改它。例如,foo可能表示当前时间。

也有可能当我写信给foo时,它与可能发生的事情几乎没有关系。例如,我可能有一个存储一些警报位的寄存器。我将写入这些位以清除它们。因此,如果一个寄存器foo具有 value 0x72,我可能会写入0x2该寄存器,这可能导致该寄存器中的结果值为0x70

volatile允许我使用这些语义。

于 2012-06-05T22:19:46.423 回答
2

Volatile 并不是一种解决多线程代码问题的方法。当变量或内存位置的值可以在编译器(甚至 CPU)没有任何方式知道的情况下更改时使用它

请参阅C++ 中的 Volatile 类以获得更完整的解释

于 2012-06-05T22:20:53.127 回答
2

新的 C 标准 C11 满足您的要求。首先,它有一个线程模型和一个延伸到线程的详细的“之前发生过”关系。它有一个atomic_thread_fence可以做你正在搜索的操作。

于 2012-06-05T22:21:16.313 回答