CPU Cache 与读写锁的关系
问题描述
在多进程操作 int8 类型数据时,为什么我们在代码里还要加读写锁?如果 CPU Cache 通过写广播和事务串行化能保证 cache 中缓存的数据的一致性,那么读写锁在这里充当了什么角色?
回答
CPU 缓存不会缓存 int8,因此 CPU Cache 与读写锁并不直接相关。对于 int8 等无法原子操作的数据类型,需要加锁保证数据的一致性。如果不加锁,不同进程访问同一份数据时可能会造成数据混乱。
CPU Cache 与缓存一致性协议
CPU 严格按照缓存一致性协议工作,对于单个指令对单个 cacheline 的操作,是可以保证原子性的。但是实际实现中,为了性能,CPU 和缓存都不是教条主义,而可能是机会主义。为了避免缓存的性能瓶颈,CPU 采用了写广播和事务串行化等技术来优化缓存性能。然而对于无法原子操作的数据类型,如 int8,需要使用读写锁等机制来保证数据的一致性。此类机制会绕过 CPU Cache 的优化,确保数据的全局可见。
代码示例
在代码中,以下操作需要加锁保证数据一致性:
- 读取 int8 数据
- 写入 int8 数据
另外,需要注意单个操作的原子性并不能保证整体操作的原子性。因此,在实现功能操作时,需要考虑整体原子性。
以下是使用锁控制共享资源的示例代码:
struct lock_t {
int flag;
};
struct lock_t lock = {0};
int main()
{
while (1) {
if (cas(lock, 0, 1) == SUCCESS) {
// 读取或写入 int8 数据
lock = 0;
break;
} else {
sleep(1);
}
}
return 0;
}
结论
CPU Cache 通过写广播和事务串行化等技术来优化性能,对于单个指令对单个 cacheline 的操作是可以保证原子性的。对于无法原子操作的数据类型,如 int8,需要使用读写锁等机制来保证数据的一致性。同时,需要注意单个操作的原子性并不能保证整体操作的原子性,在实现功能操作时,需要考虑整体原子性。