具有互斥锁和信号量的volatile关键字

时间:2017-08-13 17:31:22

标签: c multithreading pthreads

问题很简单。 使用多线程的变量是否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);
 }

示例或等效思考信号量怎么样?

3 个答案:

答案 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编译器。