【发布时间】:2011-01-15 15:34:21
【问题描述】:
是否有可能在 C 中只获取一个 int 位然后翻转它而不读取整个 int 并将其写回内存?
上下文: 试图避免 pthread 中的锁定/解锁过多。
【问题讨论】:
标签: c
是否有可能在 C 中只获取一个 int 位然后翻转它而不读取整个 int 并将其写回内存?
上下文: 试图避免 pthread 中的锁定/解锁过多。
【问题讨论】:
标签: c
你不能从内存中读取一个位,实际上你不能强制 CPU 只读取一个字节。它总是读取一个完整的缓存行,对于不同的 CPU,它可能有不同的大小。
但是从语言的角度来看,你可以使用位域http://publications.gbdirect.co.uk/c_book/chapter6/bitfields.html
【讨论】:
据我所知,最低的操作单位是字节。也许您可以将 int 拆分为字节并从 word 中读取所需的字节并仅更改那个。
【讨论】:
没有。为什么会这样,即使您可以从内存中读取一点,您仍然需要锁定以确保它是安全的。
【讨论】:
要么使用可以原子处理的最小类型(char 在任何健全的架构上都可以,但一些 RISC 垃圾不能原子地对单个字节进行操作)并接受你会浪费一些空间,使用锁,或使用正确的原子原语(在 asm、gcc 内置或 C1x _Atomic 类型中)对单个位进行原子算术。
【讨论】:
这就是你如何比爷爷互斥锁快 50 倍(我们对这种事情进行了测试)。使用 gcc 原子操作。如果您的版本包含它们,您也可以使用 linux 原子操作。
typedef union _foo {
struct {
unsigned int a:1,
b:6,
q:2;
} Data;
unsigned int n32;
} TFoo;
TFoo _GlobalFoo;
SomeFuncThatChangesFoo_A(int fNewA)
{
TFoo Old, New;
while(1) {
// get a copy of the current state
Old.n32 = _GlobalFoo.n32;
New.n32 = Old.n32;
New.Data.a = fNewA;
// compare and swap is the atomic operation.
// if _GlobalFoo hasn't changed since we copied it to "Old", then change _GlobalFoo to "New".
// __sync_bool_compare_and_swap works on basic types (8 bit, 16 bit, 32 bit, 64 bit, 128 bit
// depending upon architecture), which is why there is a union overlaying the 32 bit n32 over
// the actual data that the program uses, Data.
if (__sync_bool_compare_and_swap(_GlobalFoo.n32, Old.n32, New.n32))
break; // successfully changed
// if we get here, the global was changed by another thread, so we just loop back, get the new value
// and try again.
} // concurrency loop
}
精明的线程专家可能会认为“Old.n32 = _GlobalFoo.n32;”这一行如果编译器选择非原子方式来执行复制,则可以在竞争中复制垃圾。但是,__sync_bool_compare_and_swap 会因为当前状态的错误副本而失败,因此不会造成任何伤害。
干杯。
【讨论】:
这可能是可能的,但只能以非便携式、非标准方式,并且只有在您的平台支持的情况下。一些 ARM CPU(例如 Cortex-M3 和 M4)实现了“位带”,为地址空间区域提供位级寻址。
但一般来说,读/写的最小单元(或等效的最小可寻址单元)是char。
【讨论】: