编译时间障碍 - 编译器代码重新排序 - gcc和pthreads

时间:2012-11-24 11:35:18

标签: gcc pthreads memory-barriers

AFAIK有pthread函数作为内存屏障(例如clarifications-on-full-memory-barriers-involved-by-pthread-mutexes)。但编译时障碍是什么,即编译器(特别是gcc)是否意识到这一点? 换句话说 - 例如 - 是pthread_create()gcc不执行重新排序的原因吗?

例如在代码中:

a = 1;
pthread_create(...);

是否确定不会进行重新排序?
那些来自不同功能的调用呢?

void fun(void) {
    pthread_create(...);
    ...
}

a = 1;
fun();

是fun()还编译时间障碍(假设pthread_create()是)?
不同翻译单位的功能怎么样?

请注意,我对gcc和pthreads的一般行为规范感兴趣,不一定是x86特定的(各种不同的嵌入式平台)。
我对其他编译器/线程库行为也不感兴趣。

1 个答案:

答案 0 :(得分:3)

因为pthread_create()之类的函数是外部函数,所以编译器必须确保在调用函数之前必须完成外部函数可见的任何副作用(例如写入全局变量)。在第一种情况下的函数调用之后,编译无法将写入重新排序到a,假设a是全局的,或者可能是外部可访问的。)

这是任何C编译器都必需的行为,并且与线程无关。

但是,如果变量a是一个局部变量,编译器可能能够重新排序它,直到函数调用(a可能根本不会在内存中结束)为止。除非像a这样的地址被采取并以某种方式在外部提供(比如将其作为线程参数传递)。

例如:

int a;

void foo(void)
{
    a = 1;
    pthread_create(...);    // the compiler can't reorder the write to `a` past 
                            //  the call to `pthread_create()`

    // ...
}


void bar(void)
{
    int b;
    b = 1;
    pthread_create(...);    // `b` can be initialized after calling `pthread_create()`
                            // `b` might not ever even exist except as a something
                            //  passed on the stack or in a register to `printf()`

    printf( "%d\n", b);
}

我不确定是否有一份文件更详细地概述了这一点 - 这主要由C的'似乎'规则所涵盖。在C99中,即5.1.2.3/3“程序执行”。 C由具有序列点的抽象机器指定,其中副作用必须完成,并且程序必须遵循该抽象机器模型,除非编译器可以推断出不需要副作用。

在上面的foo()示例中,编译器通常无法推断出a = 1;不需要pthread_create()设置,因此设置a的副作用必须在调用1之前完成值pthread_create()。请注意,如果有执行全局优化的编译器可以推断a未在其他地方使用,则可能会延迟或忽略分配。但是,在这种情况下,没有其他方法可以使用副作用,因此没有问题。