为什么gcc不是';t优化全局变量?

为什么gcc不是';t优化全局变量?,c,linux,gcc,optimization,volatile,C,Linux,Gcc,Optimization,Volatile,我试图通过一个例子来理解C语言中volatile的行为和编译器优化 为此,我提到: 以上所有文章都至少有一个与信号处理程序相关的答案,因此,为了理解,我编写了一个简单的代码来实际实现和观察Linux中的行为 #include <stdio.h> #include <signal.h> #include <unistd.h> #include <pthread.h> int counter = 0; void *thread0_func(v

我试图通过一个例子来理解C语言中
volatile
的行为和编译器优化

为此,我提到:

以上所有文章都至少有一个与信号处理程序相关的答案,因此,为了理解,我编写了一个简单的代码来实际实现和观察Linux中的行为

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

int counter = 0;

void *thread0_func(void *arg)
{
    printf("Thread 0\n");
    while(1)
    {

    }
    return NULL;
}

void *thread1_func(void *arg)
{
    printf("Thread 1\n");
    while(counter == 0)
    {
        printf("Counter: %d\n", counter);
        usleep(90000);
    }
    return NULL;
}

void action_handler(int sig_no)
{
    printf("SigINT Generated: %d\n",counter);
    counter += 1;
}

int main(int argc, char **argv)
{
    pthread_t thread_id[2];

    struct sigaction sa;

    sa.sa_handler = action_handler;

    if(sigaction(SIGINT, &sa, NULL))
        perror("Cannot Install Sig handler");


    if(pthread_create(&thread_id[0], NULL, thread0_func, NULL))
    {
        perror("Error Creating Thread 0");
    }
    if(pthread_create(&thread_id[1], NULL, thread1_func, NULL))
    {
        perror("Error Creating Thread 0");
    }
    else
    {

    }
    while(1)
    {
        if(counter >= 5)
        {
            printf("Value of Counter is more than five\n");
        }
        usleep(90000);
    }
    return (0);
}
#包括
#包括
#包括
#包括
int计数器=0;
void*thread0_func(void*arg)
{
printf(“线程0\n”);
而(1)
{
}
返回NULL;
}
void*thread1_func(void*arg)
{
printf(“线程1\n”);
while(计数器==0)
{
printf(“计数器:%d\n”,计数器);
美国LEEP(90000);
}
返回NULL;
}
无效操作处理程序(内部信号编号)
{
printf(“生成的SigINT:%d\n”,计数器);
计数器+=1;
}
int main(int argc,字符**argv)
{
pthread_t thread_id[2];
struct-sigaction-sa;
sa.sa_handler=动作_handler;
if(sigaction(SIGINT,&sa,NULL))
perror(“无法安装Sig处理程序”);
if(pthread\u create(&thread\u id[0],NULL,thread0\u func,NULL))
{
perror(“创建线程0时出错”);
}
if(pthread_create(&thread_id[1],NULL,thread1_func,NULL))
{
perror(“创建线程0时出错”);
}
其他的
{
}
而(1)
{
如果(计数器>=5)
{
printf(“计数器值超过五个\n”);
}
美国LEEP(90000);
}
返回(0);
}
此代码仅用于学习和理解

我尝试使用:
gcc-O3 main.c-o main-pthread

但是编译器没有作用于全局变量
计数器
,也没有对其进行优化。
我期望
*thread1\u func
在一个永久循环中执行,并且
if(counter>=5)
永远不会为真

我错过了什么


GCC版本:
GCC版本5.4.0 20160609(Ubuntu5.4.0-6ubuntu1~16.04.4)
您对
计数器值的if测试中穿插了对
usleep
printf
的调用。这些是不透明的库调用。编译器无法看穿它们,因此必须假设它们可以访问
计数器
外部变量,因此必须在这些调用之后重新加载
计数器
变量

如果将这些调用移出,代码将按预期进行优化:

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

int counter = 0;

void *thread0_func(void *arg)
{
    printf("Thread 0\n");
    while(1)
    {

    }
    return NULL;
}

void *thread1_func(void *arg)
{
    printf("Thread 1\n");
    unsigned i=0;
    while(counter == 0)
    {
       i++;
    }
    printf("Thread 1: %d, i=%u\n", counter, i);
    return NULL;
}

void action_handler(int sig_no)
{
    printf("SigINT Generated: %d\n",counter);
    counter += 1;
}

int main(int argc, char **argv)
{
    pthread_t thread_id[2];

    struct sigaction sa;

    sa.sa_handler = action_handler;

    if(sigaction(SIGINT, &sa, NULL))
        perror("Cannot Install Sig handler");


    if(pthread_create(&thread_id[0], NULL, thread0_func, NULL))
    {
        perror("Error Creating Thread 0");
    }
    if(pthread_create(&thread_id[1], NULL, thread1_func, NULL))
    {
        perror("Error Creating Thread 0");
    }
    else
    {

    }
    while(1)
    {
        if(counter >= 5)
        {
            printf("Value of Counter is more than five\n");
        }
        usleep(90000);
    }
    return (0);
}
#包括
#包括
#包括
#包括
int计数器=0;
void*thread0_func(void*arg)
{
printf(“线程0\n”);
而(1)
{
}
返回NULL;
}
void*thread1_func(void*arg)
{
printf(“线程1\n”);
无符号i=0;
while(计数器==0)
{
i++;
}
printf(“线程1:%d,i=%u\n”,计数器,i);
返回NULL;
}
无效操作处理程序(内部信号编号)
{
printf(“生成的SigINT:%d\n”,计数器);
计数器+=1;
}
int main(int argc,字符**argv)
{
pthread_t thread_id[2];
struct-sigaction-sa;
sa.sa_handler=动作_handler;
if(sigaction(SIGINT,&sa,NULL))
perror(“无法安装Sig处理程序”);
if(pthread\u create(&thread\u id[0],NULL,thread0\u func,NULL))
{
perror(“创建线程0时出错”);
}
if(pthread_create(&thread_id[1],NULL,thread1_func,NULL))
{
perror(“创建线程0时出错”);
}
其他的
{
}
而(1)
{
如果(计数器>=5)
{
printf(“计数器值超过五个\n”);
}
美国LEEP(90000);
}
返回(0);
}
即使将计数器变量设置为static,编译器也不会进行优化,因为尽管外部库肯定看不到计数器变量,但从理论上讲,外部调用可能具有互斥锁,这将允许另一个线程在没有数据争用的情况下更改变量。现在,无论是
usleep
还是
printf
都不是互斥锁的包装器,但是编译器不知道,也不进行线程间优化,因此它必须保守,并在调用后重新加载计数器变量,而重新加载正是阻止优化的原因


当然,一个简单的解释是,如果信号处理程序执行,那么您的程序是未定义的,因为您应该使
计数器
成为volatile sig\u-atomic\u t
,并且您应该将对它的线程间访问与
\u-atomic
或互斥锁同步——在一个未定义的程序中,任何事情都有可能。

如果
操作处理程序
实际执行,则程序具有未定义的行为。为什么会这样?您正在修改
计数器
,同时从另一个线程访问它。这是不允许的。因此,解决方案是将其声明为volatile?如果没有线程,可以将其声明为
volatile sig\u atomic\t
。对于线程,它不能保证工作,您需要一个实际的同步机制(互斥、信号量或类似的机制)。我不确定你能不能从信号处理程序中使用它们。C和它的编译器真的很神秘@PSkocik谢谢!!