目录

  • 概要
  • Q1 什么是atomic?
  • Q2 alignas 关键字
  • Q3 C++的内存模型 relaxed ordering, release-acquire ordering, sequentially consistent ordering
  • Q4 atomic_compare_exchange_weak 与 atomic_compare_exchange_strong的区别
  • Q5 实现一个单生产者单消费者的队列

概要

这一章内容主要讲解无锁编程相关技能,在生产中,为了解决线程间资源共享问题,最常用的方式就是加锁了,这在上一章并发编程也讲到过很多。关于无锁的时候,也写过一篇无锁队列的实现,而今天这篇文章主要是讲解无锁编程所需要了解的基础知识,比如atomics,内存模型,内存排序等等。

Q1 什么是atomic?

std::atomic<T> x;

以上代码采用标准库的atomic类型声明了一个T类型的原子变量x。在计算机指令执行过程中,一个变量的改变包含 加载(load),更新(update)和 存储(store)3个步骤,原子变量的改变意味着这3个步骤的中间态不会被其他线程所见,避免了普通变量指令乱序带来的非意料操作。

Q2 alignas 关键字

cpu(32位)在一个时钟周期可以读取4个连续的内存单元,即4字节。使用字节对齐将会提高系统的性能,比如将一个int(4个字节)放在奇数内存位置上时,需要两个时钟周期才能读取完,而如果数据用4字节对齐之后,只需要一次就可以读取出来

以下为alignas的用法

alignas(32) long long a = 0;#define XX 1
struct alignas(XX) MyStruct {};template <size_t YY = 1>
struct alignas(YY) MyStruct {};static const unsigned zz = 1;
struct alignas(ZZ) MyStruct {};//char用int的方式对齐
alignas(int) char c;

alignas只能改大的值,而不能改小,需改小时可采用 #pragma pack 或者 _Pragma(微软暂不支持)

_Pragma("pack(1)")
struct MyStruct {char a;int b;short c;long long d;char e;
};
_Pragma("pack()")

Q3 C++的内存模型 relaxed ordering, release-acquire ordering, sequentially consistent ordering

关于c++的内存模型,分为三种: relaxed ordering(松弛次序)、acquire-release ordering(获取-释放次序)、sequentially consistent ordering(顺序一致次序),C++11 提供了6种操作(memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_reelase, memory_order_acq_rel, memory_order_seq_cst)

relaxed ordering(松弛次序),使用memory_order_relaxed标记原子变量的load和store操作,不强制其在并发环境中的内存访问顺序,只保证当前操作的原子性。

//定义全局原子变量,让其在thread1 thread2中访问
std::atomic<int> x = 0;
std::atomic<int> y =0;

thread-1

r1 = y.load(memory_order_relaxed);//A
x.store(r1, memory_order_relaxed);//B

thread-2

r2 = x.load(memory_order_relaxed);//C
y.store(1, memory_order_relaxed);//D

以上在cpu执行指令乱序的情况下有可能出现 D-》A-》B-》C的顺序,即r1 == r2 == 1.

如果某个操作值要求是原子操作,不需要其他同步保障的话,可以使用Relaxed ordering模型,比如做计数器的数值

acquire-release ordering(获取-释放次序),release与acquire操作是配对出现在希望同步语义的线程间,比如原子变量在A线程用memory_order_release标记其store操作,而在B线程用memory_order_acquire标记其load操作,即在store之前的所有读写操作不允许被移动到store之后,load之后的所有读写操作,不允许被移动到load前面。避免了cpu指令的乱序,保证了store跟load之间的读写次序。其他线程的读写次序不受影响,该咋乱咋乱。

#include <thread>
#include <atomic>
#include <cassert>
#include <string>std::atomic<bool> ready{false};
int data = 0;
void producer() {data = 100;//Aready.store(true, std::memory_order_release);//B
}void consumer() {while(!ready.load(std::memory_order_acquire));assert(data == 100);
}int main()
{std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();return 0;
}

A指令始终在B指令之前执行,如果已经退出了while,所以data == 100,那么assert不会捕获

sequentially consistent ordering(顺序一致次序),使用memory_order_seq_cst标记后的原子变量,线程间指令重排的限制与在顺序性代码中进行指令重排的限制时一致的。

Q4 atomic_compare_exchange_weak 与 atomic_compare_exchange_strong的区别

首先,这两个函数是c++11提供的CAS(compare & set)操作
原理是,比较当前值是否符合预期值,如果符合就将当前值变更为最新值,并返回true,如果不符合就将当前值更新为预期值,并返回false。伪代码如下:

if (*obj == *expected) {*obj = desired;return true;
} else {*expected = *obj;return false;
}

_weak函数在于当前值与预期值一致时,存储也可能失败,而变量的值不会改变,并且返回的是false。这被称为伪失败(spurious failure),而_strong不允许伪失败,那么它需要cpu保证这个操作指令时原子性的,在生产中,我们通常使用while(*_weak) 方式解决伪失败的同时提升处理能力。

Q5 实现一个单生产者单消费者的队列

在并发编程章节,我们采用了锁实现队列,接下来,我们采用原子变量来实现单生产者单消费者队列,避免使用锁时的性能损耗。

#include <assert.h>
#include <vector>
#include <atomic>template <typename T>
class SPSCQueue {private://cacheline大小为64,我们可采用64位内存对齐方式alignas(64) std::atomic<int> _readPos;//缓冲区读位置alignas(64) std::atomic<int> _writePos;//缓冲区写位置//64-4 填充 60位char _padding[64 - sizeof(_writePos)];//采用数组来存放队列数据std::vector<T> _buffer;private://内敛函数的作用在于编译时编译器在内联函数调用的位置嵌入函数体的内容,减少函数调用//该函数返回消息存入队列缓冲数组的位置inline int increment(int pos) const {return (pos + 1) % int(_buffer.size());}
public:SPSCQueue(int capacity) : _buffer(capacity + 1), _readPos(0), _writePos(0) {assert(capacity > 0);//capacity是个有符号值,存在溢出情况assert(capacity + 1 > 0);}bool push_back(const T &item) {//读取 readPos值,保证其他线程在load之后的读写操作不会发生在load之前const int r = _readPos.load(std::memory_order_acquire);//读取 writePos的值, relaxed保证的是当前线程的writePos值操作的顺序一致性const int w = _writePos.load(std::memory_order_relaxed);//获取写入位置const int next_w = increment(w);//写入位置等于读取位置,说明队列已经满了if (r == next_w) return false;_buffer[w] = item;//保证其他线程在store之前对writePos值的的读写操作不会发生在store之后_writePos.store(next_w, std::memory_order_release);return true;}bool pop_front(T &item) {//当前线程顺序int r = _readPos.load(std::memory_order_relaxed);//保证其他线程在load之后的读写操作不会发生在load之前int w = _writePos.load(std::memory_order_acquire);//队列为空  if (r == w) return false;item = _buffer[r];_readPos.store(increment(r), std::memory_order_release);return true;}
};

生产可用多生产者多消费者的无锁队列

交易系统开发技能及面试之无锁编程(Lock-free)相关推荐

  1. 交易系统开发技能及面试之TechCoding

    文章目录 概要 Q1设计股票订单撮合系统,支持5000以上不同品种,单品种每天上百万订单 Q2 设计订单簿,用于支持下单.撤单.撮合功能,撮合采用价优方式 Q3 设计一个网关用于接收用户订单并将其转发 ...

  2. 交易系统开发技能及面试题之c++基础特性

    文章目录 概要: Q1 空类实例化对象的sizeof Q2 指针操作优先级 *p++ Q3 c-string作为map key时会有什么问题 Q4 虚函数表的Sizeof大小 Q5 new/new[] ...

  3. 浅谈Linux内核无锁编程原理

    非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...

  4. 无锁编程(Lock Free)框架 系列文章

    无锁编程(Lock Free)框架 系列文章: 1 前置知识:伪共享 原理 & 实战 2 disruptor 使用和原理 图解 3 akka 使用和原理 图解 4 camel 使用和 原理 图 ...

  5. 【C++】多线程与原子操作和无锁编程【五】

    [C++]多线程与原子操作和无锁编程[五] 1.何为原子操作 前面介绍了多线程间是通过互斥锁与条件变量来保证共享数据的同步的,互斥锁主要是针对过程加锁来实现对共享资源的排他性访问.很多时候,对共享资源 ...

  6. 我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程

    在并行编程中,经常会遇到多线程间操作共享集合的问题,很多时候大家都很难逃避这个问题做到一种无锁编程状态,你也知道一旦给共享集合套上lock之后,并发和伸缩能力往往会造成很大影响,这篇就来谈谈如何尽可能 ...

  7. 帖子如何实现显示浏览次数_我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程...

    在并行编程中,经常会遇到多线程间操作共享集合的问题,很多时候大家都很难逃避这个问题做到一种无锁编程状态,你也知道一旦给共享集合套上lock之后,并发和伸缩能力往往会造成很大影响,这篇就来谈谈如何尽可能 ...

  8. 【翻译】RUST无锁编程

    本文内容译自Lock-freedom without garbage collection,中间有少量自己的修改. 人们普遍认为,垃圾收集的一个优点是易于构建高性能的无锁数据结构.对这些数据结构进行手 ...

  9. 无锁编程与有锁编程的效率总结、无锁队列的实现(c语言)

    1.无锁编程与有锁编程的效率 无锁编程,即通过CAS原子操作去控制线程的同步.如果你还不知道什么使CAS原子操作,建议先去查看相关资料,这一方面的资料网络上有很多. CAS实现的是硬件级的互斥,在线程 ...

最新文章

  1. ssh客户端避免超时的设置
  2. dataimagepng php_浅析data:image/png;base64的应用
  3. python爬虫实战(一)~爬取百度百科人物的文本+图片信息+Restful api接口
  4. 咖啡日报入驻PMCAFF App安卓端
  5. fprintf与fwrite的区别
  6. Bridge(桥接)--对象结构模式
  7. linux安装mysql5.5.52,Linux系统上安装MySQL 5.5prm
  8. 网络篇:朋友面试之TCP/IP,回去等通知吧
  9. git-底层-高层指令
  10. java中位运算传参_java基础之位运算讲解于实战
  11. P2839 畅通工程
  12. leetcode719:直线上的第k近点对
  13. 201521123035《Java程序设计》第十周实验总结
  14. 如何在Mac视频中删除音频呢?
  15. windows下常见php集成环境安装包介绍(updated)
  16. itextpdf 给pdf文档添加图片
  17. c语言面试(c语言面试基础知识)
  18. 理解sparse coding
  19. 蛮力法/01背包问题
  20. 给服务器上传文件高并发,基于容器实现高并发网站

热门文章

  1. Siege——压力测试
  2. 学习笔记九:BERT和它的小伙伴们
  3. 柳高人俱乐部会员参访,走进柳州772数字文化创意产业园
  4. 骨传导蓝牙耳机排行榜10强,介绍几款不错的户外骨传导耳机
  5. Android Google推荐的图片加载库Glide介绍
  6. 码农二代有多强?学区房?不存在的!
  7. 魅族MAX4 pro 手机无声音
  8. jsfiddle 国内镜像
  9. Apache Pulsar基本理论
  10. 从知网找到的一些国外较好的电子类的网站