InterlockedExchange()类型函数/内在函数的MSDN文档不一致?

时间:2012-06-06 10:56:26

标签: c visual-c++ x86 intrinsics lock-free

首先,我们有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(),注意没有volatile;

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(),在这里我们看到volatile使用了!

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

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

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

GCC的instrincs并没有提到交易所的波动,但他们也没有提到它的CAS!所以这没有帮助。

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

我也看到InterlockedIncrement()的函数是易失性的,但instrincs不是。 CAS的内在函数对其目的地来说是不稳定的。

2 个答案:

答案 0 :(得分:3)

MSDN上充斥着大量文档错误(例如,__readfsdword仅在VS 2005文档下标记为内核),您应该注意的是编译器使用的定义,在本例中, intrin.h中的定义(取自VS2010 Ultimate SP1):

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

我们可以看到它们确实是volatile指针所必需的。

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

答案 1 :(得分:1)

这些函数不是指向volatile的指针 ,它是允许的。也就是说,如果参数声明为long *而不是long volatile *,则传递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作为一种表明变量可能被其他线程修改的方式有着悠久的传统。这是错误的,因为volatile实际上并没有为多线程提供有意义的有用语义,但是当C ++没有承认多线程开发人员变得绝望时。使用volatile的问题在于它不会阻止编译器或CPU重新排序,因此它在多线程代码中有99%的时间是错误的。

缺少C ++ 11安全的做法是只使用Interlocked *函数引用这些线程共享变量。如果你这样做,那么不需要volatile。或者,像一个理智的人一样使用锁。

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