如果我有一些看起来像这样的代码:
typedef struct { bool some_flag; pthread_cond_t c; pthread_mutex_t m; } foo_t; // I assume the mutex has already been locked,and will be unlocked // some time after this function returns. For clarity. Definitely not // out of laziness ;) void check_flag(foo_t* f) { while(f->flag) pthread_cond_wait(&f->c,&f->m); }
C标准中是否有任何内容阻止优化器将check_flag重写为:
void check_flag(foo_t* f) { bool cache = f->flag; while(cache) pthread_cond_wait(&f->c,&f->m); }
换句话说,每次通过循环时生成的代码是否必须遵循f指针,或者编译器是否可以自由地取消引用?
如果它可以自由拉出来,有什么办法可以防止这种情况发生吗?我需要在某处撒一个volatile关键字吗?它不能是check_flag的参数,因为我计划在这个结构中有其他变量,我不介意编译器这样优化.
可能我不得不诉诸:
void check_flag(foo_t* f) { volatile bool* cache = &f->some_flag; while(*cache) pthread_cond_wait(&f->c,&f->m); }
解决方法
通常,您应该在等待条件对象之前尝试锁定pthread互斥锁,因为pthread_cond_wait调用释放互斥锁(并在返回之前重新获取它).所以,你的check_flag函数应该被重写,以符合pthread条件下的语义.
void check_flag(foo_t* f) { pthread_mutex_lock(&f->m); while(f->flag) pthread_cond_wait(&f->c,&f->m); pthread_mutex_unlock(&f->m); }
关于是否允许编译器优化标志字段的读取的问题,这个answer比我更详细地解释了它.
基本上,编译器知道pthread_cond_wait,pthread_mutex_lock和pthread_mutex_unlock的语义.他知道在这种情况下他无法优化内存读取(在这个例子中调用pthread_cond_wait).这里没有记忆障碍的概念,只是对某些功能的特殊知识,以及在他们面前遵循的一些规则.
还有一件事可以保护您免受处理器执行的优化.您的平均处理器能够重新排序内存访问(读/写),前提是语义是守恒的,并且它总是这样做(因为它允许提高性能).但是,当多个处理器可以访问相同的内存地址时,这会中断.内存屏障只是处理器的指令,告诉它可以移动在屏障之前发出的读/写并在屏障之后执行它们.它现在已经完成了.