概要

本文是无锁同步系列文章的第一篇,主要探讨C++11中的Atomic。

我们知道在C++11中引入了mutex和方便优雅的lock_guard。但是有时候我们想要的是性能更高的无锁实现,下面我们来讨论C++11中新增的原子操作类Atomic,我们可以利用它巧妙地实现无锁同步。

传统的线程同步

#include <thread>
#include <mutex>#include <iostream>using namespace std;mutex g_mutex;
int g_count = 0;int main()
{thread thr1([]() {for (int i = 0;i < 5;i++) {lock_guard<mutex> lock(g_mutex);    //①g_count += 10;}});thread thr2([]() {for (int i = 0;i < 5;i++) {lock_guard<mutex> lock(g_mutex);    //②g_count += 20;}});thr1.join();thr2.join();cout << g_count << endl;33 }

在上述例子中,如果把①②的锁注释后,我们可能无法得到正确的结果。原因是C++并没有给我们保证+=操作具有原子性(其本质应该是读-加-写3个操作)。

Atomic

C++11给我们带来的Atomic一系列原子操作类,它们提供的方法能保证具有原子性。这些方法是不可再分的,获取这些变量的值时,永远获得修改前的值或修改后的值,不会获得修改过程中的中间数值。

这些类都禁用了拷贝构造函数,原因是原子读和原子写是2个独立原子操作,无法保证2个独立的操作加在一起仍然保证原子性。

这些类中,最简单的是atomic_flag(其实和atomic相似),它只有test_and_set()和clear()方法。其中,test_and_set会检查变量的值是否为false,如果为false则把值改为true。

除了atomic_flag外,其他类型可以通过atomic获得。atomic提供了常见且容易理解的方法:

store
load
exchange
compare_exchange_weak
compare_exchange_strong

store是原子写操作,而load则是对应的原子读操作。

exchange允许2个数值进行交换,并保证整个过程是原子的。

而compare_exchange_weak和compare_exchange_strong则是著名的CAS(compare and set)。参数会要求在这里传入期待的数值和新的数值。它们对比变量的值和期待的值是否一致,如果是,则替换为用户指定的一个新的数值。如果不是,则将变量的值和期待的值交换。

weak版本的CAS允许偶然出乎意料的返回(比如在字段值和期待值一样的时候却返回了false),不过在一些循环算法中,这是可以接受的。通常它比起strong有更高的性能。

例子

下面举个简单的例子,使用CAS操作实现一个不带锁的并发栈。这个例子从《C++并发编程》摘抄而来。

Push

在非并发条件下,要实现一个栈的Push操作,我们可能有如下操作:

  1. 新建一个节点
  2. 将该节点的next指针指向现有栈顶
  3. 更新栈顶

但是在并发条件下,上述无保护的操作明显可能出现问题。下面举一个例子:

  1. 原栈顶为A。(此时栈状态: A->P->Q->…,我们约定从左到右第一个值为栈顶,P->Q代表p.next = Q)
  2. 线程1准备将B压栈。线程1执行完步骤2后被强占。(新建节点B,并使 B.next = A,即B->A)
  3. 线程2得到cpu时间片并完成将C压栈的操作,即完成步骤1、2、3。此时栈状态(此时栈状态: C->A->…)
  4. 这时线程1重新获得cpu时间片,执行步骤3。导致栈状态变为(此时栈状态: B->A->…)

结果线程2的操作丢失,这显然不是我们想要的结果。

那么我们如何解决这个问题呢?只要保证步骤3更新栈顶时候,栈顶是我们在步骤2中获得顶栈顶即可。因为如果有其它线程进行操作,栈顶必然改变。我们可以利用CAS轻松解决这个问题:如果栈顶是我们步骤2中获取顶栈顶,则执行步骤3。否则,自旋(即重新执行步骤2)。因此,不带锁的压栈Push操作比较简单。

template<typename T>
class lock_free_stack
{private:struct node{T data;node* next;node(T const& data_):data(data_){}};std::atomic<node*> head;
public:void push(T const& data){node* const new_node=new node(data);new_node->next=head.load();while(!head.compare_exchange_weak(new_node->next,new_node));}
};

我们可以注意到一个非常巧妙的设计。在push方法里,atomic_compare_exchange_weak如果失败,证明有其他线程更新了栈顶,而这个时候被其他线程更新的新栈顶值会被更新到new_node->next中,因此循环可以直接再次尝试压栈而无需由程序员更新new_node->next。

Pop
发现原文Pop部分有错误,所以暂时删除

无锁同步-C++11之Atomic和CAS相关推荐

  1. C++11 - 无锁队列

    无锁操作的本质依赖的原子操作,C++11提供了atomic的原子操作支持 atomic compare_exchange_weak / compare_exchange_strong 当前值与期望值相 ...

  2. Java并发编程,无锁CAS与Unsafe类及其并发包Atomic

    为什么80%的码农都做不了架构师?>>>    我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其 ...

  3. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  4. 基于数组的无锁队列(译)

    2019独角兽企业重金招聘Python工程师标准>>> 1 引言 最近对于注重性能的应用程序,我们有了一种能显著提高程序性能的选择:多线程.线程的概念实际上已经存在了很长时间.在过去 ...

  5. 无锁链表的c++实现

    简言 1.无锁能够实现的核心原理即CAS 2. c++语言中CAS一般有三种操作,即gcc的CAS实现,windows的CAS实现,c++11的CAS实现 3. 这篇博客主要讲c++11的CAS实现, ...

  6. 【重难点】【JUC 04】synchronized 原理、ReentrantLock 原理、synchronized 和 Lock 的对比、CAS 无锁原理

    [重难点][JUC 04]synchronized 原理.ReentrantLock 原理.synchronized 和 Lock 的对比.CAS 无锁原理 文章目录 [重难点][JUC 04]syn ...

  7. C++线程编程-设计无锁的并发数据结构

    定义和结果 使用互斥元.条件变量以及future 来同步数据的算法和数据结构被称为阻塞的算法和数据结构.调用库函数的应用会中断一个线程的执行,直到另一个线程执行一个动作.这种库函数调用被称为阻塞调用, ...

  8. CAS无锁队列的实现

    文章目录 1. 基本原理 2. 代码实现 2.1 使用链表实现无锁队列 2.2 使用数组实现环形无锁队列 3. ABA 问题及解决 4. 参考资料 1. 基本原理 源于1994年10月发表在国际并行与 ...

  9. [数据结构]——无锁队列

    (152条消息) [数据结构]--无锁队列_lucky52529的博客-CSDN博客_无锁队列无锁队列 写这篇博客前想声名以下几点.第一,这篇文章重点内容是关于无锁队列如何实现,并不会深入讲解底层的C ...

  10. 深入理解并发编程之CAS无锁机制与ABA问题

    深入理解并发编程之CAS无锁机制与ABA问题 文章目录 深入理解并发编程之CAS无锁机制与ABA问题 前言 一.什么是CAS无锁机制 二.CAS原理分析 1.AtomicLong自增分析 2.基于At ...

最新文章

  1. TensorRT Samples: MNIST(Plugin, add a custom layer)
  2. 拿来就能用! CTO 创业技术栈指南!
  3. 【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第二步)(4)...
  4. Xamarin XAML语言教程Xamarin.Forms中改变活动指示器颜色
  5. 关于在Windows Mobile下今日插件使用WTL的问题
  6. Ubuntu上安装oracle java 7
  7. ./configure --with-package=dir指定依赖的软件包
  8. Ionic APP 热更新
  9. html是什么1002无标题,Jsf页面为普通/无标题的文本html
  10. java数字转换32位字符串_java字符串和数字转换工具
  11. Base64序列化和反序列化
  12. 树形dp贪吃的九头龙(vijos1523)
  13. 正则表达式的语法及使用
  14. wav怎么转换成mp3?
  15. Windows防火墙无法更改某些设置
  16. 人工智能在医学影像中的研究与应用
  17. html页面宽度1920,网页banner尺寸1920
  18. 英译汉文章在线翻译器:批量翻译、素材收集、图片处理
  19. 【Vitis Accel】2 - Vitis 应用加速开发平台简介
  20. PyQt5 从零开始环境搭建

热门文章

  1. 手把手教你进行R语言的安装及安装过程中相关问题解决方案
  2. 读取topic数据存储到文件内
  3. 【BIEE】MDS-01377:无法从使用 JNDI 名称 “jdbc/mds/owsm” 配置的数据源获取数据库连接...
  4. 洛谷P3358 最长k可重区间集问题(费用流)
  5. base command
  6. Sublime Text 插件 【转】
  7. 通过kafka提供的命令来查看offset消费情况
  8. nosql笔记1_早前的时候
  9. 12.性能之巅 洞悉系统、企业与云计算 --- 基准测试
  10. 34. HTTP服务的七层架构技术解析及运用