互斥操作函数参数应该是“volatile”吗?
Should the mutex manipulation functions parameter be `volatile`?
我正在使用自定义嵌入式实时 OS,它具有一些自制的线程和同步工具。互斥的实现方式类似于下面概述的方式:
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
是如何更改的?
是否应该使用 volatile
限定符来声明 AquireMutex
:
bool AquireMutex(volatile Mutex* pMutex);
答案取决于使用 while 循环的调用站点是否可以看到这些函数的实现。如果不是(调用站点只看到声明,定义在单独的源文件中),那么 volatile
关键字将不会改变任何内容。优化器完全不知道这个函数对参数做了什么,它是否有副作用等等,所以每次函数调用都会进行。
另一方面,如果释放和获取互斥锁的函数是内联的——那么完整实现在调用站点上是可见的——那么实际上优化器可能"tweak" 事情有点多了。问题出在 "complete" 字中 - 即使您发布的代码是内联的,开始和结束关键部分的代码也可能不是。即使是,它也可能使用优化器无法理解的汇编语句。即使它是纯 C,它也可能访问一些易失性内存映射寄存器。任何此类代码(调用外部函数、汇编语句、访问易失性内存)有效地禁止优化器消除所有调用,因为在这种情况下,它必须假设每个调用都有副作用。
我正在使用自定义嵌入式实时 OS,它具有一些自制的线程和同步工具。互斥的实现方式类似于下面概述的方式:
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
是如何更改的?
是否应该使用 volatile
限定符来声明 AquireMutex
:
bool AquireMutex(volatile Mutex* pMutex);
答案取决于使用 while 循环的调用站点是否可以看到这些函数的实现。如果不是(调用站点只看到声明,定义在单独的源文件中),那么 volatile
关键字将不会改变任何内容。优化器完全不知道这个函数对参数做了什么,它是否有副作用等等,所以每次函数调用都会进行。
另一方面,如果释放和获取互斥锁的函数是内联的——那么完整实现在调用站点上是可见的——那么实际上优化器可能"tweak" 事情有点多了。问题出在 "complete" 字中 - 即使您发布的代码是内联的,开始和结束关键部分的代码也可能不是。即使是,它也可能使用优化器无法理解的汇编语句。即使它是纯 C,它也可能访问一些易失性内存映射寄存器。任何此类代码(调用外部函数、汇编语句、访问易失性内存)有效地禁止优化器消除所有调用,因为在这种情况下,它必须假设每个调用都有副作用。