6

我知道位域依赖于编译器,但我没有找到有关最新 g++ 和 Visual C++ 2010 位域线程安全的文档。

位域成员的操作是原子的吗?

4

5 回答 5

6

不幸的是,“线程安全”是编程中一个非常重载的术语。

如果您的意思是对位域的原子访问,答案是否定的(至少在我知道的所有处理器上)。您可以原子访问 32 位机器上的 32 位内存位置,但这仅意味着您将读取或写入整个 32 位值。这并不意味着另一个线程不会做同样的事情。如果您想停止,您可能需要同步。

如果您的意思是对位域的同步访问,答案也是否定的,除非您将访问包装在更高级别的同步原语中(通常建立在原子操作之上)。

简而言之,如果您没有额外的工作,编译器不会提供对位字段的原子同步访问。

这有帮助吗?

编辑Dan Grossman 博士有两节关于原子性和同步性的精彩讲座,我在 UOregon 的 CS 部门页面上找到

于 2011-05-31T19:51:42.183 回答
4

写入位域时,可能存在一个时间窗口,在该时间窗口中,另一个线程访问(读取或写入)同一结构中的任何(相同或不同)位域的任何尝试都将导致未定义行为,这意味着任何事情都可以发生。读取位域时,可能存在一个时间窗口,当另一个线程尝试在同一结构中写入任何位域时,将导致未定义行为。

如果您实际上不能为所讨论的位域使用单独的变量,您可以将多个位域存储在一个整数中,并通过在位域结构和 32 位整数之间创建联合来自动更新它们,并且然后使用 CompareExchange 序列:

  1. 将位域的值读取为 Int32。
  2. 将其转换为位域结构
  3. 更新结构
  4. 将结构转换回 Int32。
  5. 仅当变量仍保持在 (1) 中读取的值时,才使用 CompareExchange 用新值覆盖该变量;如果值已更改,请从步骤 (1) 重新开始。

为了使这种方法运作良好,步骤 2-4 必须快速。它们花费的时间越长,步骤 5 中的 CompareExchange 失败的可能性就越大,因此在 CompareExchange 成功之前必须重新执行步骤 2-4 的次数越多。

于 2011-05-31T20:16:49.030 回答
1

如果要以线程安全的方式更新位域,则需要将位域拆分为单独的标志并使用常规ints 来存储它们。访问单独的机器字是线程安全的(尽管您需要考虑多处理器系统上的优化和缓存一致性)。

于 2011-05-31T19:40:35.273 回答
1

请参阅 Windows联锁功能

另请参阅此相关的 SO 问题

于 2011-05-31T19:50:01.293 回答
0

只是简单地使用 atomic.AddInt32 示例:

atomic.AddInt32(&intval, 1 << 0)      //set the first bit
atomic.AddInt32(&intval, 1 << 1) //set the second bit
atomic.AddInt32(&intval, -(1 << 1 + 1 << 0)) //clear the first and second bit

代码在 Go 中,我认为 c++ 也有类似 atomic.AddInt32 的东西

于 2012-10-28T02:11:29.957 回答