螺纹安全和钻头领域

Thread safety and bit-field

本文关键字:安全      更新时间:2023-10-16

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

对位字段成员的操作是原子操作吗?

不幸的是,

"线程安全"在编程中是一个非常重载的术语。

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

如果您的意思是同步对位字段的访问,那么答案也是否定的,除非您将访问封装在更高级别的同步原语中(通常基于原子操作)。

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

这有帮助吗?

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

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

如果您实际上不能为有问题的位字段使用单独的变量,则可以将多个位字段存储在一个整数中,并通过在位字段结构和32位整数之间创建并集,然后使用CompareExchange序列进行原子更新:

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

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

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

请参阅Windows互锁函数

另请参阅此相关SO问题

只需简单使用原子。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++也有一些类似原子的东西。AddInt32