C语言 InterlockedExchange() 类型函数/内部函数的 MSDN 文档不一致



首先,我们有InterlockedExchange64();

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683593%28v=vs.85%29.aspx

LONGLONG __cdecl InterlockedExchange64( __inout LONGLONG volatile *Target, __in LONGLONG Value );

其次,我们有编译器内部函数,_InterlockedExchange64(),注意没有易失性;

http://msdn.microsoft.com/en-us/library/1s26w950%28v=vs.80%29.aspx

__int64 _InterlockedExchange64( __int64 * Target, __int64 Value );

接下来,我们有 InterlockedExchangePointer(),它与 InterlockedExchange64() 一样,使用 volatile。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683609%28v=vs.85%29.aspx

PVOID __cdecl InterlockedExchangePointer( __inout  PVOID volatile *Target, __in PVOID Value );

但是现在我们来到指针交换的内在,_InterlockedExchangePointer(),在这里我们看到使用了易失性!

http://msdn.microsoft.com/en-us/library/853x471w.aspx

void * _InterlockedExchangePointer( void * volatile * Target, void * Value );

底层指令在所有情况下都是相同的,那么什么给出呢? 文档错误?

海湾合作委员会(GCC)的机构没有提到交换的易失性,但是他们也没有提到CAS的易失性! 所以这无济于事。

我的观点是,CAS 目标是易失性的,因为你只能在运行时知道交换是否会发生;但原子交换不应该是易失性的,因为目标总是更新的(即使它的值不会改变),所以编译器没有不确定性。

我还看到 InterlockedIncrement() 的函数是易失性的,但 instrincs 不是。 CAS 的内在因素对于其目的地具有波动性。

MSDN充斥着大多数小的文档错误(例如,__readfsdword仅在VS 2005文档下被标记为内核),您真正应该注意的是编译器使用的定义,在这种情况下,intrin.h中的定义(取自VS2010 Ultimate SP1):

__MACHINEI(long _InterlockedExchange(long volatile *, long)) 
__MACHINEIA64(__int64 _InterlockedExchange64(__int64 volatile *, __int64))

正如我们所看到的,它们确实是所需的volatile指针。

最后要注意的是,您的所有链接都是VS 2005文档(默认情况下由Google链接到较旧的内部函数),因此请确保使用页面顶部的下拉菜单切换到最新版本。

这些函数不需要指向易失性的指针,而是允许它。也就是说,如果参数声明为 long* 而不是 long volatile*,则传递易失变量的地址将给出此错误:

cannot convert argument 1 from 'volatile LONGLONG *' to 'LONGLONG *'

这可以通过以下简单的代码看到:

LONGLONG a;
volatile LONGLONG b;
void DoSomething(LONGLONG* p) {}
int main() {
  DoSomething(&a);
  DoSomething(&c); // Error!
  return 0;
}

C/C++ 中有一个悠久的传统,即滥用 volatile 作为指示变量可能被其他线程修改的一种方式。这是错误的,因为易失性实际上并没有为多线程提供有意义的有用语义,但是当C++不承认多线程开发人员感到绝望时。使用易失性的问题在于它不会阻止编译器或 CPU 重新排序,因此在多线程代码中 99% 的时间都是错误的。

如果没有 C++11,安全的做法是只使用互锁* 函数引用这些线程共享变量。如果这样做,则不需要易失性。或者,像理智的人一样使用锁。

但是,由于许多开发人员使用原子标记其线程共享变量,因此互锁*函数必须接受这些变量。这就是为什么它们都采用指针到易失性的类型。

相关内容

  • 没有找到相关文章

最新更新