我正在使用具有一些家庭酿造线程和同步功能的自定义嵌入式实时操作系统。互斥是以类似于下面概述的方式实施的:
typedef int Mutex;
#define MUTEX_ACQUIRED 1
#define MUTEX_RELEASED 0
bool AquireMutex(Mutex* pMutex)
{
bool Ret;
// Assume atomic section here (implementation specific)
if( *pMutex == MUTEX_RELEASED )
{
*pMutex = MUTEX_ACQUIRED;
Ret = true;
}
else
{
Ret = false;
}
// Atomic section end
return Ret;
}
void ReleaseMutex(Mutex* pMutex)
{
// Assume atomic section here (implementation specific)
*pMutex = MUTEX_RELEASED;
// end atomic section
}
让我们假设上面的两个函数是原子的(在实际实现中它们是,但实际的实现与问题无关)。
每个线程共享一些全局定义的m
并且代码类似于此:
extern Mutex m;
// .............
while (!AquireMutex(&m)) ;
// Do stuff
ReleaseMutex(&m);
问题在于:
while (!AquireMutex(&m)) ;
每次迭代是否会实际评估AquireMutex
?或者优化器会将其视为常量,因为它不会看到m
如何更改?
是否应使用AquireMutex
限定符声明volatile
:
bool AquireMutex(volatile Mutex* pMutex);
答案 0 :(得分:3)
答案取决于使用while循环调用站点是否可以看到这些函数的实现。如果不是(调用站点只看到声明,定义在单独的源文件中),那么volatile
关键字将不会改变任何内容。优化器完全不知道这个函数对参数的作用,它是否有副作用等等,所以每个函数调用都会被调用。
另一方面,如果释放和获取互斥锁的功能是内联的 - 所以在呼叫站点上可以看到完整实现 - 那么确实优化器可能会“调整”一些内容。问题在于“完整”一词 - 即使您发布的代码是内联的,开始和结束关键部分的代码可能也不是。即使它是,它也可能使用优化器无法理解的汇编语句。即使它是纯C,它也可能访问一些易失性的内存映射寄存器。任何这样的代码(调用外部函数,汇编语句,访问易失性存储器)都会有效地阻止优化器消除所有调用,因为在这种情况下它必须假设每个调用都有副作用。