问题很简单。 使用多线程的变量是否volatile
甚至可以在C中的临界区(即互斥,信号量)中访问?为什么/为什么不呢?
#include <pthread.h>
volatile int account_balance;
pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
void debit(int amount) {
pthread_mutex_lock(&flag);
account_balance -= amount;//Inside critical section
pthread_mutex_unlock(&flag);
}
示例或等效思考信号量怎么样?
答案 0 :(得分:2)
对于多线程使用的变量是否应该是易变的,即使在C中的临界区(即互斥,信号量)中访问?为什么/为什么不呢?
没有
volatile
在逻辑上无关紧要,因为它不就足够了。
实际上,这并不是真的 - volatile
并不是无关紧要的,因为它可以隐藏代码中的并发问题,因此它可以正常运行&#34;大多数情况下&#34} #34;
所有volatile都告诉编译器&#34;这个变量可以在当前执行线程之外改变&#34;。挥发性决不会强制执行任何排序,原子性或 - 关键 - 可见性。仅仅因为CPU A上的线程2发生了变化int x
,这并不意味着CPU D上的线程1甚至可以在任何特定时间看到更改 - 它有它自己的缓存值,volatile
在内存一致性方面几乎没有任何意义,因为它不能保证排序。
英特尔文章底部的最后一条评论:Almost Useless for Multi-Threaded Programming说得最好:
如果您只是简单地添加“易失性”&#39;到共享的变量 在线程之间认为可以解决您的共享数据问题 为了理解为什么不这样做,你最终会收获 奖励你应得的。
是的,无锁代码可以使用volatile
。这些代码是由那些可能编写有关使用volatile
,多线程代码和其他关于编译器的非常详细的主题的教程的人编写的。
答案 1 :(得分:1)
不,volatile
不应用于在pthread_mutex_lock()
等pthreads同步函数的保护下访问的共享变量。
原因是POSIX保证同步函数本身提供所有必要的编译器障碍和同步以确保一致性(只要您遵循并行访问的POSIX规则 - 即您已使用pthreads同步函数确保没有线程可以写入共享变量,而另一个线程正在写入或读取它。)
答案 2 :(得分:0)
我不知道为什么互联网上到处都有关于易失性的错误信息。您的问题的答案是是,您应该在关键部分中使用易变的变量。
我会给出一个人为的例子。假设你想在多个线程上运行这个函数:
int a;
void inc_a(void) {
for (int i = 0; i < 5; ++i) {
a += 5;
}
}
在这个网站上,每个人都会告诉你,在一个关键部分加上+ = 5就足够了:
int a;
void inc_a(void) {
for (int i = 0; i < 5; ++i) {
enter_critical_section();
a += 5;
exit_critical_section();
}
}
正如我所说,它是人为的,但人们会告诉你这是正确的,而绝对不是!如果编译器没有事先了解关键部分的功能,它们的语义是什么,没有什么能阻止编译器输出这段代码:
int a;
void inc_a(void) {
register eax = a;
for (int i = 0; i < 5; ++i) {
enter_critical_section();
eax += 5;
exit_critical_section();
}
a = eax;
}
此代码在单线程上下文中生成相同的输出,因此允许编译器执行此操作。但是在多线程上下文中,这可以输出线程数的25到25倍之间的任何内容。解决此问题的一种方法是使用原子构造,但这会产生性能影响,而您应该做的是使变量变为volatile。也就是说,除非你想要像这个社区的其他人一样,并盲目地相信你的C编译器。