文章目录

  • 前言
  • 一、互斥锁
    • 1.mutex
    • 2.lock_guard
    • 3.unique_lock
  • 二、条件变量
    • condition_variable
  • 三、信号量
    • semaphore
  • 四、异步操作
    • 1.async构造方式
    • 2.future
    • 3.promise
  • 五、原子操作
  • 备注

前言

关于C++多线程编程的几种实现方式(互斥锁、条件变量、信号量、异步操作、原子操作)小结


一、互斥锁

使用锁的方式对共享资源对象的访问进行控制,操作包括上锁lock()、解锁unlock()

1.mutex

  • include <mutex>
  • lock和unlock必须成对出现
  • 构造出来的mutex对象都是unlock状态
  • mutex不允许拷贝构造
  • 当前线程lock了mutex,该线程在unlock之前一直拥有该锁,被锁期间阻塞。如果是自身线程调用了lock锁住mutex,可能会产生死锁
  • try_lock(),尝试锁,即使被占有也不会阻塞(失败会返回false)
  • mutex为最简单的互斥锁,不支持递归上锁,需要递归上锁要使用recursive_lock类

2.lock_guard

  • 构造方式:lock_guard<mutex> lck(mtx);
  • 相比较mutex最大特点是,构造时自动lock,离开作用域后调用析构函数unlock,可以与智能指针类比
  • 采用资源请求初始化(RAII)编程技术,针对动态分配的资源(需要申请内存,非static对象)
  • 不能手动解锁,不能拷贝

3.unique_lock

  • 比lock_guard功能更加强大,但开销也大
  • 构造时选择加锁方式:defer_lock延迟加锁;try_lock尝试加锁;adopt_lock立刻加锁
  • release()函数,返回管理的mutex对象指针,释放所有权(不能再对该mutex进行控制),之后用户可以对mutex进行手动unlock操作,此功能给予更大的编程自由度,从而完成相应功能
  • 配合condition_variable使用,condition_variable wait()函数第一个参数为unique_lock类型
  • 不允许复制,但允许"move",即对mutex的所有权进行传递,lck1对mymutex所有权转移至lck2:
    unique_lock<mutex>lck1(mymutex);
    unique_lock<mutex> lck2(mymutex);

二、条件变量

条件不满足,线程被阻塞。一个线程等待某一变量的成立而阻塞,另一线程使该变量变化而使上一线程成立,同时发送信号(notify)唤醒wait的线程

condition_variable

  • 获取mutex,确切的说为mutex构造的unique_lock对象
  • wait_for(),wait_until(),第二个参数为chrono(时间库)
  • wait_for(),wait()条件成立 or 超过给定时间段的时间自动unlock
  • wait_until(),同上,但时间段改为时刻
  • notify_all(),notify_one(),全部唤醒 or 唤醒之一

三、信号量

提供原子操作,C语言提供

semaphore

  • include <semaphore.h>
  • 定义:sem_t mysem;
  • 初始化:int sem_init(sem_t *sem,int pshared,unsigned int value); value 参数指定信号量的初始值。一般:sem_init(&mysem,0,0);需要首次执行的:sem_init(&mysem,0,1);

pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。

  • int sem_wait(sem_t *sem); //sem+1 >0时终端阻塞,继续执行
  • int sem_post(sem_t *sem); //sem-1
  • int sem_trywait(sem_t *sem);
  • int sem_getvalue(sem_t *sem);

四、异步操作

在不需要等待被调用方返回之前,继续进行后续操作。c++11提供了异步接口std::async,std::async会自动创建一个线程去调用 线程函数,它返回一个std::future,这个future中存储了线程函数返回的结果,当我们需要线程函数的结果时,直接从future中获取

1.async构造方式

  • template <class Fn, class… Args> future<typename
    result_of<Fn(Args…)>::type>
    async (Fn&& fn, Args&&… args);

  • template <class Fn, class… Args> future<typename
    result_of<Fn(Args…)>::type>
    async (launch policy, Fn&& fn, Args&&… args);

  • policy:
    launch::async: 异步启动一个新线程调用fn
    launch::deferred延迟:对 fn 的调用被延迟,直到返回的未来的共享状态被访问(等待或获取)。 此时,调用 fn 并且该函数不再被视为延迟。 当此调用返回时,返回的未来的共享状态已准备就绪。
    launch::async|launch::deferred:该函数自动选择策略(在某一点)。这取决于系统和库实现,它们通常针对系统中当前的并发可用性进行优化。

  • fn 函数指针,可以为仿函数、lambda表达式,类的成员函数等

  • args 传递给fn的参数

2.future

  • future是一个可以从某个提供者对象或函数中检索值的对象,如果在不同的线程中,可以正确地同步这种访问
  • template future;
    template <class R&> future<R&>;
    template <> future<void>;
  • future是与共享状态相关联的未来对象,并且通过调用以下函数之一来构造:
    async
    promise::get_future
    packaged_task::get_future
  • get()取值、valid()共享是否有效、wait()阻塞等待(由valid判断,false阻塞,true继续执行)、wait_for()、wait_until()同上

3.promise

  • promise是一个对象,它可以存储一个T类型的值,供将来的对象(可能在另一个线程中)检索,提供一个同步点。
  • 这个共享状态可以通过调用成员get_future关联到一个未来对象。调用后,两个对象共享相同的共享状态: promise对象是异步提供者,应该在某个时候为共享状态设置一个值。 future对象是一个异步返回对象,它可以检索共享状态的值,并在必要时等待它准备就绪。
  • set_value()设置value并通知future

五、原子操作

原子操作能够保证多个线程顺序访问,不会导致数据争用,执行时没有任何其它线程能够修改相同的原子对象。原子操作更加接近底层,因而效率更高。

  • std::atomic<T>构造对象
  • atomic_long total(0);
  • atomic<bool> flag{ false }
  • 底层原理:自旋锁 + CAS(乐观锁)
  • 乐观锁:更新值时进行compareAndSwap操作,即当内存当前值等于内存预期值时,将要更新的值赋给内存,不等时不操作,之后可重试
  • 自旋锁:尝试获取锁的所有权时会以忙等待的形式不断的循环检查锁是否可用

备注

以上为实际遇到的问题,待补充

C++多线程编程的几种实现方式小结相关推荐

  1. Qt实现多线程编程的两种方式

    Qt实现多线程编程的两种方式 方式一: 继承自QThread类,覆写run函数.此实现方法只有run函数内的代码是运行在子线程内. 代码示例: #ifndef QDEMOTHREAD_H #defin ...

  2. 操作系统课设——编程演示三种管理方式的地址换算过程

    核心内容 编程演示三种管理方式的地址换算过程 分页方式的地址换算 分段方式的地址换算 段页式的地址换算 概括 程序主要有三个功能,分别是 分页方式的地址换算,分段方式的地址换算,段页式的地址换算. S ...

  3. Spring中的AOP切面编程的三种实现方式

    文章目录 Spring中的AOP切面编程的三种实现方式 1.最基本AOP的实现 a.引入jar包 b.编写通知类,这里以后置通知和环绕通知类为例子进行说明 c.在SpringIOC容器中配置 d.测试 ...

  4. mybatis plus当月数据查询_mybatis plus的3种查询方式(小结)

    本文是基于springboot框架下的查询. 一:基本配置: 1.仓库依赖 alimaven aliyun maven http://maven.aliyun.com/nexus/content/gr ...

  5. AOP面向切面编程的三种实现方式

    AOP概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是 ...

  6. android 响应点击事件,Android响应事件onClick方法的五种实现方式小结

    在Android的开发中,对于点击事件的OnClickListener有下面四种实现方式,可以根据实际场景的需要选择合适的用法.下面以Button按钮来举例说明. 方法一: 适合场景:任何场景都通用, ...

  7. oauth2四种授权方式小结

    序 本文主要小结一下oauth2的四种模式的特点和适用场景. 四种授权方式 OAuth 2.0定义了四种授权方式. 密码模式(resource owner password credentials) ...

  8. vue暂存功能_vue路由缓存的几种实现方式小结

    本文实例讲述了vue路由缓存的几种实现方式.分享给大家供大家参考,具体如下: 在我们的日常开发中,有时候会遇到页面的缓存,特别是电商的项目,在商品列表的一些状态都是要缓存下来的. 下面就简单介绍几种 ...

  9. 聊聊异步编程的 7 种实现方式

    大家好,我是 Tom哥 最近有很多小伙伴给我留言,能不能总结下异步编程,今天就和大家简单聊聊这个话题. 早期的系统是同步的,容易理解,我们来看个例子 同步编程 当用户创建一笔电商交易订单时,要经历的业 ...

最新文章

  1. 机器学习最常用的优化算法 — 梯度下降法
  2. Lync Server 2010移动功能部署PART B:外部篇
  3. python定义一个圆_Python-矩形和圆形
  4. php歌声美化_PHP代码在线格式化美化工具
  5. 5月18发布会,这次TDSQL又有什么大动作?
  6. Elasticsearch对地理数据查询(一)
  7. iterator and iterable
  8. pig连接oracle数据库,Pig安装讲解
  9. mysql like 贪婪匹配_mysql模糊查询like与REGEXP的使用详细介绍
  10. python求平均工资_python如何求列表平均值?
  11. VS2010SP1安装失败解决方法
  12. php mysql mysql_set_charset()._PHP:MySQL函数mysql_set_charset()的用法
  13. Java读取文本文件中文乱码问题
  14. Java I/O(输入输出流)
  15. Linux设备驱动程序和设备文件
  16. 2019北京中考英语口语计算机考试,2019北京中考英语听说考试体验系统发布,附考试流程和注意事项...
  17. C语言深度解剖读书笔记
  18. 学习Photoshop的一些网站以及找素材的网站
  19. 五年级下册计算机知识点,五年级下册信息技术知识点(教材解析泰山版)
  20. Android 9.0 framework 授予安装未知来源应用权限(去掉未知来源弹窗)

热门文章

  1. oracle自动加载项,exceloracle加载项
  2. 快上车!通用自主开发无人驾驶汽车首次公开试驾 | 精选
  3. 基础算法与数据结构——页面置换算法
  4. 小型软件企业 出路_6个适用于小型企业的最佳会计软件(比较)
  5. java初学者常用单词
  6. 在Exchange Server 2010中启用分层地址薄
  7. js获取上个月今天或下个月今天
  8. webi安装ubuntu13.1时,选择空间为30G安装好后空间只有11.9G?
  9. 哪款蓝牙耳机性价比高?南卡和华为蓝牙耳机对比测评
  10. 02 Xcp协议层介绍