线程安全和位域

6

我知道位域(bit-field)是与编译器相关的,但我没有找到关于最新的g++和Visual C++ 2010中位域在线程安全方面的文档。

对位域成员进行的操作是否是原子的?


2
“Thread safe”是什么意思? - user2100815
1
@NeilButterworth:你回来了吗?我假设他在问一个位域成员的操作是否是原子操作。 - the_drow
我的意思是,不同的线程可以在没有干扰的情况下访问或分配不同的成员。 @the_drow:没错,我会更正这个问题。 - Errata
1
那么你需要对位域进行原子读/写操作。如果你想要严格的读/写顺序,你需要对位域进行同步访问。听起来你可能两者都需要。 - user7116
5个回答

6
“线程安全”是编程中一个非常重载的术语。
如果你指的是对位域的原子访问,则答案是否定的(至少在我所知道的所有处理器上)。在32位机器上,您可以对32位内存位置进行原子访问,但这仅意味着您将读取或写入整个32位值。这并不意味着另一个线程不会做同样的事情。如果您想要停止这种情况,您可能需要同步。
如果您指的是对位域的同步访问,则答案也是否定的,除非您将访问封装在更高级别的同步原语中(这些通常建立在原子操作之上)。
简而言之,编译器不提供对位字段的原子或同步访问,除非您在自己的代码中进行额外的工作。
这有帮助吗?
编辑:Dr. Dan Grossman在UOregon的CS系网页上有两个很好的关于原子性和同步性的讲座

5
当写入位域时,可能存在一个时间窗口,在此期间,另一个线程尝试访问(读取或写入)同一结构中的任何(相同或不同的)位域,将导致未定义行为,意味着任何事情都可能发生。当读取位域时,可能存在一个时间窗口,在此期间,另一个线程尝试写入同一结构中的任何位域将导致未定义行为。
如果您无法实际使用针对所涉及的位域的单独变量,则可以将多个位域存储在整数中,并通过创建位域结构和32位整数之间的联合体来原子地更新它们,然后使用CompareExchange序列:
1. 将位字段的值作为Int32读取。 2. 将其转换为位域结构 3. 更新结构 4. 将结构转换回Int32。 5. 仅当变量仍保持读取(1)中的值时,使用CompareExchange将其覆盖为新值;如果值已更改,则重新开始执行步骤(1)。
为了使此方法有效,步骤2-4必须快速完成。它们花费的时间越长,步骤5中CompareExchange失败的可能性就越大,因此在CompareExchange成功之前,步骤2-4将不得不重新执行多次。

1

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


你的意思是机器字的访问是原子操作吗? - user7116

1

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的函数。


1
Go语言不是C++。为了让这个回答有用,你需要用C++来解释它。 - jogojapan
如果位已经设置,添加操作将无效。您必须先测试该位是否已设置。 - uliwitness

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接