编程学习
🗒️C++多线程编程(6):原子操作
00 分钟
2023-11-20
2023-11-23
type
status
date
slug
summary
tags
category
icon
password
Email
🏡
我的个人主页:https://www.helloylh.com/
📕
文章首发于我的个人博客:https://blog.helloylh.com/
💖
欢迎大佬们来逛逛,有任何问题欢迎给我留言或者加我的联系方式。

atomic

引入:两个线程对同一个变量执行10000次的自增,则最后的结果会是20000吗?显然这是不可能的,结果一定是 ≤20000 的。
因此C++11中的 atomic 头文件给我们定义了一个模板类,允许我们进行原子操作。
什么是原子操作?
  • 原子操作是不可中断的操作,它在执行过程中不会被其他任务或线程干扰
  • 在多线程或并发编程中,原子操作对于确保数据的一致性和避免竞态条件是非常重要的。

使用atomic解决上面的问题的程序:
定义原子类型的变量:
允许进行原子操作的类型有:
原子变量的基本操作:
  • 直接使用 = 赋值,或者使用构造函数进行赋予初值。
  • store:为变量赋予新值。这个函数的第一个参数表示即将赋予的新值,第二个参数表示内存顺序
  • load:读取变量的值。
  • exchange:为变量赋予新值,并且返回旧值
  • compare_exchange_weak:
  • fetch_add:

内存顺序

其中内存顺序具有以下取值:
  • std::memory_order_relaxed: 操作不会引入任何同步操作,只保证原子性。其他线程可能以不同的顺序观察到这些操作,因此在此模式下,不提供全局同步保证。
  • std::memory_order_consume: 该选项已在C++11中被弃用,C++20中移除。它是为了支持消费模式(consume模式)而设计的,但在实际中不太常用。
  • std::memory_order_acquire: 在load操作之前的所有读取-修改-写入操作必须在此之前完成。这样可以确保在load之后,之前的所有写入对该线程可见。
  • std::memory_order_release: 在store操作之后的所有读取-修改-写入操作必须在此之后开始。这样可以确保在store之前,该线程的所有写入对其他线程可见。
  • std::memory_order_acq_rel: 结合了acquirerelease的效果,适用于既有读取-修改-写入操作又有写入-读取操作的情况。
  • std::memory_order_seq_cst(Sequentially Consistent): 提供最强的同步保证,确保所有线程看到的操作顺序都是一致的。但是,它可能会引入较大的性能开销。

举个最简单的例子:
对于这个例子,我们创建了两个线程,第一个线程负责对data和ready赋值,第二个线程负责打印输出,如果我们赋予store和load不同的参数则可能会发生什么?
  • std::memory_oder_relaxed:对于ready的true赋值可能会发生在对于data的赋值之前进行,而导致循环终止,则输出结果:Data: 0. 但是则显然是错误的!
  • std::memeory_order_release:其确保在写入ready标志位之后,之前的所有修改对其他线程可见。因此最后输出:Data: 100
  • std::memory_order_seq_cst写入和读取的顺序是保证一致的。因此,最终输出的结果应该是:Data: 100

无锁队列实现

无锁队列(Lock-Free Queue)是一种并发数据结构,旨在在多线程环境中实现高性能的队列操作而无需使用传统的互斥锁(mutex)来进行同步。在无锁队列中,多个线程可以同时执行队列的入队(enqueue)和出队(dequeue)操作,而不需要显式的锁定。
使用无锁队列的主要目标是提高并发性能,减少由于锁竞争而引起的性能瓶颈。在传统的锁基础上的队列中,当一个线程在对队列进行操作时,其他线程必须等待锁的释放,这可能导致性能下降,尤其是在高并发情况下。
无锁队列通常基于原子操作实现,这意味着队列的操作可以在没有锁的情况下以原子方式完成。
常见的原子操作包括 Compare-and-Swap (CAS) 和 Fetch-and-Add 等。
对于普通的带锁队列的效率:
可以发现无锁队列避免了重复的加锁与解锁,比带锁队列在效率上有了一定的提升。

评论
  • Twikoo
  • Valine