如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
考虑一个简单的(在我的情况下是全局)变量:
int i;
Run Code Online (Sandbox Code Playgroud)
在某处访问此变量
pthread_mutex_lock(i_mutex);
if(i == other value) {
do_something();
}
pthread_mutex_unlock(i_mutex);
Run Code Online (Sandbox Code Playgroud)
另一个线程i 在保持时更新i_mutex.编译器可以缓存值,i所以我没有得到最近的值?一定i是不稳定的?
看着经过一大堆 的 其他 问题 和 他们的 答案,我得到的印象是有什么在C“挥发性”关键字表示正好没有广泛的协议。
即使标准本身似乎也不够清晰,以至于每个人都无法理解其含义。
除其他问题外:
总结一下问题,似乎(经过大量阅读)“ volatile”保证了类似的结果:该值将不但从/向寄存器,而且至少向内核的L1缓存中读/写,其顺序与读/写出现在代码中。但这似乎没有用,因为在同一线程内从寄存器中读取/写入寄存器已经足够,而与L1缓存进行协调并不能保证与其他线程进行协调。我无法想象仅与L1缓存进行同步的重要性。
用途1
唯一广泛同意使用volatile的似乎是旧的或嵌入式系统,其中某些内存位置通过硬件映射到I / O功能,例如内存中的某个位(直接在硬件中)控制灯光。 ,或内存中的某个位告诉您键盘键是否按下(因为它是通过硬件直接连接到键的)。
看来,“用1”不移植的代码,其目标包括多核系统发生。
USE 2
与“ use 1”没什么不同,是可由中断处理程序(可以控制灯光或存储来自按键的信息)随时读取或写入的内存。但是为此已经存在一个问题,即取决于系统,中断处理程序可能会在 具有自己的内存缓存的不同内核上运行,并且“ volatile”不能保证所有系统上的缓存一致性。
因此,“使用2”似乎超出了“易失性”所能提供的范围。
用途3
我看到的唯一其他无可争议的用途是防止通过指向编译器未意识到的同一内存的不同变量的不同变量对访问进行错误优化。但这可能只是无可争议的,因为人们没有在谈论它-我只看到其中一个提及。而且我认为C标准已经认识到“不同”的指针(例如指向函数的不同args)可能指向同一项目或附近的项目,并且已经指定编译器必须生成即使在这种情况下也可以工作的代码。但是,我无法在最新的标准(500页!)中快速找到此主题。
那么“使用3”也许根本不存在?
因此,我的问题是:
在多核系统的可移植C代码中,“ volatile”是否可以保证任何东西?
浏览最新标准后,答案似乎至少是非常有限的:
1.该标准针对特定类型“ volatile sig_atomic_t”反复指定特殊处理。但是该标准还说,在多线程程序中使用信号功能会导致不确定的行为。因此,该用例似乎仅限于单线程程序与其信号处理程序之间的通信。
2.该标准还为setjmp / longjmp指定了“ volatile”的明确含义。(在其他问题和答案中给出了重要示例代码)。
因此,更精确的问题变成了:
除了(1)允许单线程程序从其信号处理程序接收信息之外,还是(2)允许setjmp,“ volatile”对于多核系统的便携式C代码是否有任何保证?代码以查看在setjmp和longjmp之间修改的变量?
这仍然是一个是/否问题。
如果为“是”,那么最好显示一个无错误的可移植代码示例,如果省略了“ volatile”,则该示例会出现错误。如果为“ no”,那么我认为对于多核目标,在这两种非常特殊的情况下,编译器可以随意忽略“ volatile”。
注意!
我显然没有向这里的每个人清楚地表明我的观点,这令人非常沮丧.我的目标是打消那种
volatile实际上是无操作的神话,它什么都不做.我并没有试图说它应该被使用,它是必不可少的,它不是多余的,等等.我已经表明
volatile它仍然可以做一件事.我承认在某些情况下它是多余的,并且多线程示例是一个糟糕的选择.我也没有试图隐瞒我的答案的初始修订包含错误这一事实.但这个Q&A甚至没有达到其预期目的.为此,我认为是时候把它扔掉了.
感谢Kerrek和TC的见解.我只是不认为他们的回答符合我想问的问题.我很确定这是我的错,因为它很糟糕.
所以我放弃了!并将其作为问题的副本而不是它的意图,但它被解释为.
干杯! (&hth.)
我正在写一个线程中的变量并在另一个线程中读取它.我被告知这对我volatile来说完全无用,除非我正在使用硬件,否则我不需要在这个时代使用它.
int x = 0;
void thread1()
{
while (true) {
sleep(1);
if (x > 0)
break;
}
}
void thread2()
{
while (true) {
sleep(1);
x++;
}
}
Run Code Online (Sandbox Code Playgroud)
volatile在这种情况下,我可以获得任何收益吗?
如果x不是一个简单int的类而是一个类类型怎么样?
据我所知,函数调用充当编译器障碍,但不作为CPU障碍.
本教程说明如下:
获取锁意味着获取语义,而释放锁意味着释放语义!其间的所有内存操作都包含在一个漂亮的小屏障三明治中,防止任何不希望的内存重新排序跨越边界.
我假设上面的引用是关于CPU重新排序而不是编译器重新排序.
但我不明白互斥锁和解锁如何导致CPU赋予这些函数获取和释放语义.
例如,如果我们有以下C代码:
pthread_mutex_lock(&lock);
i = 10;
j = 20;
pthread_mutex_unlock(&lock);
Run Code Online (Sandbox Code Playgroud)
上面的C代码被翻译成以下(伪)汇编指令:
push the address of lock into the stack
call pthread_mutex_lock()
mov 10 into i
mov 20 into j
push the address of lock into the stack
call pthread_mutex_unlock()
Run Code Online (Sandbox Code Playgroud)
现在是什么阻止了CPU重新排序mov 10 into i以及mov 20 into j 上方call pthread_mutex_lock()或下方call pthread_mutex_unlock()?
如果它是call阻止CPU进行重新排序的指令,那么为什么我引用的教程使它看起来像是互斥锁和解锁函数来阻止CPU重新排序,为什么我引用的教程没有说任何函数调用会阻止CPU重新排序吗?
我的问题是关于x86架构.