【问题标题】:Win API Interlocked operations for 32-bit int type32 位 int 类型的 Win API 互锁操作
【发布时间】:2010-09-03 15:09:53
【问题描述】:

如果我们有:

__int32 some_var = 0;

调用InterlockedExchangeInterlockedIncrement 和其他需要LONG* 用于some_var 的联锁功能的最佳(如果有)方法是什么?

由于可以保证LONG 在任何Windows 上都是32 位的,因此通过(long*) some_var 可能是安全的。但是,在我看来,这很丑陋,我无法确认它是安全的。

注意,我无法将类型更改为long,因为它不可移植。我需要 32 位类型。

更新:对提供可移植原子操作的库的一些研究表明,没有人会担心强制转换。一些例子:

Apache Portable Runtime (APR):

typedef WINBASEAPI apr_uint32_t (WINAPI * apr_atomic_win32_ptr_val_fn)
    (apr_uint32_t volatile *, 
     apr_uint32_t);

APR_DECLARE(apr_uint32_t) apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val)
{
#if (defined(_M_IA64) || defined(_M_AMD64))
    return InterlockedExchangeAdd(mem, val);
#elif defined(__MINGW32__)
    return InterlockedExchangeAdd((long *)mem, val);
#else
    return ((apr_atomic_win32_ptr_val_fn)InterlockedExchangeAdd)(mem, val);
#endif
}

atomic_ops:

AO_INLINE AO_t
AO_fetch_and_sub1_full (volatile AO_t *p)
{
  return _InterlockedDecrement64((LONGLONG volatile *)p) + 1;
}

【问题讨论】:

标签: c++ c multithreading winapi interlocked


【解决方案1】:

嗯,这是一块岩石和坚硬的地方。原子增量是重型平台实现细节。这就是为什么首先存在 LONG typedef 的原因。 20 或 50 年后的某些未来操作系统可能会重新定义该类型。例如,当 256 位内核很常见并且原子增量的工作方式不同时。谁知道呢。

如果您想编写真正可移植的代码,那么您应该使用真正可移植的类型。喜欢龙。让它发挥作用将是微软的负担,而不是你的。

在很长一段时间内它都会是一个 32 位整数,我建议你不要担心。

【讨论】:

  • @Hans:为什么提供InterlockedExchange32 是个问题?有 64 位整数的函数,它们适用于 __int64 类型。
  • @Hans:LONG 根本不可移植。它只适用于 MS,但我需要 32 位便携类型。
  • @Hans:(LONG *) &some_var 有效且安全吗?如果安全,编译器为什么不自动生成?
  • 如果您不想依赖 Win32,请不要使用它的 API。 Boost 可以提供帮助,interlocked.hpp 标头。完全相同的问题,它很长。出于完全相同的原因。
  • 我怀疑在你解释为什么它必须是一个 32 位整数之前,这不会真的去任何地方。
【解决方案2】:

您不妨将类型更改为 long,留下可移植性,因为整个“互锁”原子操作系列也不可移植

顺便说一句,我认为 interlocked 支持整数重载。也许那只是在 .net 中。

【讨论】:

  • 刚才我说过我不能更改类型。但是我可以(并且这是常用的方式)编写可移植的MyPortableInterlockedExchangeWrapper(当然,在 Windows 上,有一种安全的方式可以将 int 传递给InterlockedExchange)。
【解决方案3】:

好吧,__int32 也不是可移植类型。所以我让问题消失的建议是使用 typedef。在 Windows 上,您可以:

typedef LONG my_int32;

...并安全地将指向此类类型的指针传递给InterlockedExchange()。在其他系统上,使用 32 位类型 - 例如,如果他们有 stdint.h,您可以这样做:

typedef int32_t my_int32;

【讨论】:

  • 我们已经在使用stdint.h(Windows 也有实现)。我使用__int32 只是为了简单起见。
  • 我不喜欢仅仅因为一个功能而引入新类型,但可能这是唯一的方法。
【解决方案4】:

只要做assert(sizeof(LONG) == sizeof(some_var)) 并且只在断言失败时才担心问题。 YAGNI。只要断言成立,就可以使用reinterpret_cast<LONG*>(&some_var)

【讨论】:

  • 大小现在在 Windows 上是相等的(我相信永远都是)。假设这是安全的,为什么?
  • @Shcheklein,我假设指向相同大小的整数类型的指针可以安全地相互转换,因为机器表示是相同的;即使它们不相同,例如有符号/无符号不匹配,差异也应该是无害的。我想LONG 有可能包含一些 Microsoft 特定的对齐关键字,所以我不能做出任何绝对保证。这都是特定于实现的巫术,因此您将不得不依赖一些假设并接受一些妥协。
  • 我相信,你是对的。至少似乎没有人为选角而烦恼。另请参阅问题更新。
  • @Shcheklein,他们在某些情况下正在进行转换,但它是旧式 C 转换而不是 C++ 风格转换。
【解决方案5】:

有趣的是,InterlockedExchange - 一个需要 LONG* 的 Windows API 和 _InterlockedExchange 一个 msvc 编译器内在函数,需要很长时间*。

由于调用了可移植性,我还会在GCC atomic intrinsics上链接一个页面。

然而,这一点很好理解:MSVC 使用 ILP32LLP64 data model 进行 32 位构建,使用 LLP64 进行 64 位构建。 Windows 确实存在基于 GCC 的工具链(例如 MinGW),并且可以很好地实现 LP64 模型——这很有趣!例如 'long' 是 64 位,但 LONG 是 32。

如果您坚持使用 Microsoft 编译器,则无需担心。

所以,总而言之: 1. 被传递的值必须用 'volatile' 限定。 2. 因为您 (a) 使用 32 位数量(这是您的要求)和 (b) 使用 InterlockedXXX API 的显式 32 位形式 - 它 100% 安全地进行血腥转换并完成它:InterlockedIncrement将对所有位大小的 32 位值进行操作,您的变量将在所有位大小上显式为 32 位 - 即使使用不同的数据模型。

演员是安全的,不要无缘无故地把事情复杂化。

【讨论】:

    【解决方案6】:

    Hans Passant表达的很好:

    “原子增量是一个重要的平台实现细节。”

    这就是实现提供特定类型重载的原因。

    atomic_ops 就是这样一个项目。

    理论上,每个 Interlocked 功能都可以通过使用成熟的锁来实现 - 这反过来又依赖于平台细节 :-) - 但这对于目标硬件平台上支持的类型和功能来说是一种真正的性能过度杀伤.

    在这方面正在进行一些标准化,参见例如herehere 也回答了类似的问题。

    【讨论】:

      猜你喜欢
      • 2010-10-26
      • 1970-01-01
      • 2013-10-31
      • 2011-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多