我是唯一这么认为的人吗? [真正的问题在一秒钟内。]
除了对它的混乱,以及它与互斥锁或其他锁定机制的混合,当我处理线程安全情况时,我总是不得不放弃使用volatile
,因为它根本不做任何事情是有用的。
volatile
“禁止对读/写进行任何重新排序或缓存”,但只要volatile
只标记单个对象(并且为taints),这就无用了它,因为它不再是一个“正常”的对象。)
考虑一下:
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
类型。您(he)表示锁定互斥锁(至少是POSIX互斥锁)是编译器识别并正确管理的东西,要么是对库的调用(编译器无法访问),可能会改变世界,所以编译器在调用之后不会假设任何东西。我说这是依赖于实现的,非常低级别的东西(我不想浏览开发文档以进行日常编程)。更糟糕的是,如果由于某种原因,出于某种原因,“外部库”由于任何合法的原因而变得可供编译器访问(可能是作者在必须包含在头文件中的模板中转换函数......无论如何),它会突然改变
所以,在我看来,volatile
完全没用(并且误导),除非是一些非常低级别的东西(设备读取可能,但我不能胜任这样的领域)。
更好的方法是将显式告诉编译器,它必须放弃任何任何变量的假设。后续读取必须是从内存中读取的真实内容。但是我无法想象比调用虚拟外部函数更好的事情,这会产生我之前概述的相同问题。有优雅的方法吗?
答案 0 :(得分:8)
我使用低级别的东西,这是一个有用的例子。
volatile uint32_t *foo = <<<a register on a pci device>>>
有可能在多次阅读foo
时,我会得到不同的结果。这是因为外部源(pci设备)正在修改它。例如,foo
可能意味着当前时间。
当我写信foo
时,它可能与可能出现的内容几乎无关。例如,我可能有一个存储一些警报位的寄存器。我会写那些位来清除它们。因此,如果寄存器foo
具有值0x72
,我可能会将0x2
写入寄存器,这可能会导致该寄存器中的结果值为0x70
。
volatile
允许我使用这些语义。
答案 1 :(得分:2)
易失性并不是一种神奇的多线程代码问题。当变量或内存位置的值可以在没有编译器(甚至CPU)知道的情况下改变时使用它
有关更全面的解释,请参阅Volatile classes in C++
答案 2 :(得分:2)
新的C标准C11符合您的要求。首先,它有一个线程模型和一个详细的“之前发生”关系,它扩展到线程。并且它有atomic_thread_fence
操作,可以执行您要搜索的内容。