你能原子地写两个uint64_t吗?

时间:2013-10-16 03:54:02

标签: c++ multithreading

我遇到一个问题,我需要能够同时原子地更新两个uint64_t。很容易以原子方式编写每一个(例如有两个std::atomic<uint64_t>),但这仍然会导致一个更新但另一个不更新的情况。使用锁和互斥锁也很容易实现。

但是我想原子地编写,没有任何锁定,所以我仍然可以拥有类型为uint64_t的成员变量,这样就没有锁定读取。这是因为我的用例涉及阅读它们很多很多次,但写得非常少(〜读1x / ms,写1x / 5分钟)。可能吗?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:3)

对于std::atomic标准说(强调我的)

  

主要std::atomic模板可以使用任意TriviallyCopyable类型T 进行实例化:

struct Counters { int a; int b; }; // user-defined trivially-copyable type
std::atomic<Counters> cnt;         // specialization for the user-defined type

所以你可以像这样创建一个2 uint64_t的结构

struct atomic128 {
    uint64_t a1, a2;
};

可以轻松复制(使用std::is_trivially_copyable很容易确认),然后使用std::atomic<atomic128>。您将收到错误if std::atomic<type> is not trivially copyable

这样编译器会自动使用无锁更新机制(如果可用)。无需做任何特别的事情,只需在必要时检查以下任何一项

  

除了std :: atomic_flag之外的所有原子类型都可以使用互斥锁或其他锁定操作来实现,而不是使用无锁原子CPU指令。原子类型也允许有时无锁:例如,如果只有一些子体系结构支持给定类型的无锁原子访问(例如x86-64上的CMPXCHG16B指令),则原子是否是无锁的可能不是直到运行时才知道。

     

std::atomic_is_lock_freestd::atomic::is_lock_free

编译器资源管理器上的

Here's a demo。如您所见,lock cmpxchg16b已发出,但GCC 7及以上只会调用__atomic_store_16 long double std::atomic<long double>

在某些平台上{{1}}是128位类型或填充到128位,因此{{1}}可能是另一种解决方案,但当然您需要检查其大小以及它是否无锁或不首先

另一种选择是internally use cmpxchg16b if it's available。它还有要检查的宏Boost.Atomic

相关:

在某些CPU上,128位SSE操作也是原子操作,遗憾的是,无法确定是否可以使用它

答案 1 :(得分:1)

我认为不可能直接做,但你可以做的是使用software transactional memory techniques来伪造它。特别是,您可以使用uint64_t-pairs的无锁环缓冲区。在这种配置中,写入环形缓冲区的非活动元素是非安全的非原子化,因为没有人会从环形缓冲区的那个元素读取,直到“current-value-index”在原子上更新为止写入结束(这是可能的,因为索引可以是int32_t)。

警告:这个技巧只有在你可以保证在短时间内没有太多的值写入时才有效(其中“太多”意味着'超过环形缓冲区中的插槽数) )。另外我建议找一个实现这个的STM库,而不是自己编写,因为无锁编程很难100%正确。