易变和多线程?

时间:2010-07-30 15:00:52

标签: c++ c multithreading pthreads

在以下代码中:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int ready = 0;

wait()
{
    int i;
    do
    {
        usleep(1000);
        pthead_mutex_lock(&mutex);
        i = ready;
        pthread_mutex_unlock(&mutex);
    } while (i == 0);   
    printf("Finished\n");
}

signal()
{
    pthead_mutex_lock(&mutex);
    ready = 1;
    pthread_mutex_unlock(&mutex);
}

我们生成两个线程,我们在一个线程中调用wait,然后在另一个线程中调用signal我们也让编译器积极地进行优化。

现在代码的行为是否符合预期,或者我们是否需要使用volatile来使其工作?不同的编译器和库会以不同的方式处理它吗?

编辑:我希望互斥函数可能存在一些阻止优化的问题,或者编译器通常不会优化循环函数调用。

注意:我还没有编译和测试代码,如果有机会的话会这样做。

6 个答案:

答案 0 :(得分:2)

如果编译器在存在库函数调用时假设有关全局变量的任何内容,我会感到惊讶。话虽这么说,不稳定不会花费你任何东西,它显示了你的意图。我把它放在那里。

答案 1 :(得分:2)

答案 2 :(得分:1)

挥发既不是必需的,也不是充分的。所以没有理由使用它。这是不够的,因为没有标准指定它将提供线程之间的可见性。没有必要,因为pthreads标准说互斥量就足够了。

更糟糕的是,使用它表明你是一个不称职的程序员,他试图在你的代码上撒上魔法粉尘来使它工作。它充斥着货物崇拜节目,任何看过代码的人都会推断出你不知道它不是必需的。更糟糕的是,他们可能认为你认为这已经足够了,并且他们会怀疑你编写的任何其他代码,担心你使用“volatile”来隐藏多线程错误而不是修复它们。

答案 3 :(得分:0)

  

现在代码的行为是否符合预期,或者我们是否需要使用volatile来使其工作?

我建议在这种情况下使用volatile。虽然在这种情况下似乎不需要它。

IOW,我个人会添加volatile并删除锁定:设置/读取变量的值不需要它。

  

不同的编译器和库会以不同的方式处理吗?   我希望互斥函数可能有一些东西会阻止自身优化,或者编译器通常不会优化循环函数调用。

在您的情况下,对函数的调用(pthread_mutex_lock())具有副作用并更改执行环境。因此编译器必须重新读取可能通过调用函数而改变的全局变量。

确实,你想咨询C99的5.1.2.3 Program execution,我借用了这个术语。给你的味道:

  

[...]访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,它们是状态的变化执行环境。表达的评估可能产生副作用。在执行序列中称为序列点的某些特定点,先前评估的所有副作用应完整,并且不会发生后续评估的副作用。 (序列点的摘要见附件C.)

     

在抽象机器中,所有表达式都按语义指定进行评估。实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用)。 [...]

摘自附件C:

  

以下是5.1.2.3中描述的序列点:
   - 在评估参数之后调用函数(6.5.2.2)。

从此开始。

根据我的经验,编译器现在足够聪明,即使在积极优化时也不会对循环控制变量(全局变量)做任何事情。

答案 4 :(得分:-1)

是的,这里总是需要volatile int ready = 0;

<强>更新
如果您不想围绕某些代码片段进行优化,可以在该代码周围使用#pragma指令(如果您的编译器支持它们)或者 - 这是完全可移植的 - 将代码移动到单独的文件并编译这些文件或者很少的优化 后一种方法可能仍然需要使用volatile关键字,因为ready变量可能会被您可能想要优化的其他模块使用。

答案 5 :(得分:-1)

在优化存在的情况下需要挥发性。否则,就绪的读取可以合法地移出while循环。

假设标准未承诺的优化限制现在可能没问题,但随着编译器的改进,将给未来的维护者带来极大的悲痛。