【C++11】并发库

📝前言:

这篇文章我们来讲讲C++11并发库中的常用对象和接口。

适合有Linux线程基础的人观看,主要简述一下C++并发库中的各种接口的用法。对于常用的东西写多了自然记住了,对于不常用的在使用的时候再去查文档就可以了。

🎬个人简介:努力学习ing

📋个人专栏:C++学习笔记

🎀CSDN主页 愚润求学

🌄其他专栏:C语言入门基础python入门基础python刷题专栏Linux


文章目录

一、thread库

  • thread库底层是对各个系统的线程库进行封装,同时是面向对象的,对许多接口的使用进行了简化

thread库文档

cpp 复制代码
default (1)	
thread() noexcept;
initialization (2)	
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)	
thread (const thread&) = delete;
move (4)	
thread (thread&& x) noexcept;
  1. 创建空线程对象
  2. thread(可调用对象,参数)创建线程
  3. 不支持拷贝构造
  4. 支持移动构造(比如把匿名线程对象移动构造给已存在的空的线程对象)

还有一些其他接口,如 join()get_id()detach()...

函数call_once

原型:

cpp 复制代码
template <class Fn, class... Args>
  void call_once (once_flag& flag, Fn&& fn, Args&&... args);
  • 多线程执行时,让第⼀个线程执行Fn⼀次(flag会被设置),其他线程不再执行Fn

二、this_thread命名空间

this_thread文档

主要有四个函数(文档讲的很清楚)

  • get_id():用于在执行流里面获得当前执行流(线程对象)的ID(因为在执行流里面没有线程对象,无法直接调对线里面的get_id方法)
  • yield:用于把线程挂起,放在同优先级线程的队列的队尾
  • sleep_untilBlocks the calling thread until abs_time(阻塞到绝对时间)
  • sleep_forBlocks execution of the calling thread during the span of time specified by rel_time(阻塞一段时间)

那怎么设置这个rel_time(一段时间间隔)呢

Dutation类(这是一个类模板),chrono时间库里面的type就是通过typedef duration来的

使用示例:

cpp 复制代码
std::this_thread::sleep_for (std::chrono::seconds(1)); // 睡眠 1s

怎么获取绝对时间呢?

我们可以通过system_clock::now()获得当前时间,当前时间 + 时间片段 == 下一个绝对时间

三,future

future对象可能让一个线程去异步执行函数,并且提供了一种机制,允许一个线程等待另一个线程的计算结果,而不必显式地处理线程同步问题。

1. std::future

创建线程异步执行任务
async:异步执行任务并返回一个std::future对象,我们可以通过future对象来获得返回结果

cpp 复制代码
template< class Function, class... Args >
std::future<typename std::result_of<Function(Args...)>::type>
async( std::launch policy, Function&& f, Args&&... args );
  • policy:执行策略(默认``)。
    • launch::async:直接创建线程异步执行
    • launch::deferred:延迟执行,直到调用future.get()future.wait()时才执行
  • f:要执行的可调用对象(函数、lambda 等)。
  • args:传递给f的参数。

packaged_task

用于包装任务,里面也包装有future对象,我们可以通过里面的future对象来拿到返回值。

promise

创建promise对象,会自动分配共享状态,可以通过get_future拿到里面的future对象

future对象

  • get():获取异步操作的结果(主线程会阻塞到到异步的线程直到结果就绪)。
    • future在关联异步操作的时候状态变有效,调用后 future 对象变为无效(也是就get只能调一次)
    • std::shared_future<T>:允许多个对象共享同一个异步操作的结果,可以被复制
  • wait():等待异步操作完成,但不获取结果。
  • wait_for():等待指定时间,返回操作状态(readytimeoudeferred)。
  • valid():检查 future 是否有效(是否关联了异步操作)。

四、mutex

mutex文档

1. 基本介绍

  • mutex:C++ 中封装的互斥锁的类,接口lockunlock不说了,其他接口文档讲的也很清楚(和Linux的pthread锁没什么区别)
  • try_lock:申请锁失败后返回false(不阻塞),所以如果后面代码是临界区代码则可能执行有安全风险

传锁问题:

  • 如果我们有一把局部锁(在主线程中),想要通过thread的构造函数(通过传引用)传给从线程是不行的。
  • 因为thread(&mutex)的时候,是把mutex传给了thread的拷贝构造
  • 但是,thread底层封装的是pthread_createpthread_create接收的是void*类型的参数
  • 所以传入的mutex以及其他参数会被打包成一个struct(这个过程中发生了拷贝),导致不是原来的锁了

解决方法:使用ref()来传参 或者 直接使用lambda来捕抓mutex

2. 其他mutex

  • timed_mutex:主要是提供try_lock_for/until
    • try_lock_for:在这个时间段内不断获取锁,拿不到就返回false(不阻塞)
    • try_lock_until:尝试获取锁,直到指定的时间点
  • recursive_mutex用于有递归的执行流
    • 普通的mutex,如果在递归的时候二次申请锁了(递归的上一层锁没释放),就把自己搞死锁了
    • recursive_mutex在申请锁的时候会对比申请的线程的ID是不是当前持有锁的线程的ID,如果是就不会再申请,避免阻塞(自己锁自己)。
  • recursive_timed_mutex:不多说了

3. lock 和 try_lock函数

常用来避免死锁问题(通过释放锁)

  • lock():是一个函数模板,接收可变参数。可以一次锁多个互斥量
    • 如果不能全部同时锁住,就先全解锁,然后阻塞
    • 如果能全部锁住,那就全部锁住了,然后继续执行
  • try_lock()
    • 全部锁对象都锁定了,返回-1
    • 如果某⼀个锁对象尝试锁定失败,把已经锁定成功的锁对象解锁,并则返回这个对象的下标(下标从0开始计算)

五、lock_guard 和 unique_lock

  • lock_guard:使用RAII设计思想的,用生命周期来管理锁 的一个类
    • 原型:template <class Mutex> class lock_guard;
  • unique_lock:有更多功能的lock_gurad
    • 原型:template <class Mutex> class unique_lock;
    • 在构造的时候,可以传入不同的tag来控制锁定行为
      • 比如当前要管理的锁之前已经锁定过了,就可以unique_lock(Mutex& m, std::defer_lock_t),代表构造时不锁定
    • unique_lock还可以通过operator bool去检查是否lock了锁对象

为什么要用这种RAII的方式呢?我们可以回忆一下智能指针,智能指针可以预防在mallocfree的时候出现异常,导致出现异常安全(就是因为异常malloc的空间没办法释放了)的情况。而lock_guard可以解决,lockunlock因为异常安全无法unlock的情况。

六,condition_variable

  • condition_variable需要配合互斥锁系列进⾏使⽤,主要提供waitnotify接口
  • wait需要传递⼀个unique_lock<mutex>类型的互斥锁,wait会阻塞当前线程直到被notify
    • 进入wait的时候会自动释放锁
    • 重新被唤醒的时候会自动申请锁
  • notify_one唤醒在当前条件变量上等的一个,notify_all唤醒在该条件变量上的全部
  • condition_variable_any 类是 std::condition_variable 的泛化。
    • 相对于只在std::unique_lock<std::mutex> 上工作的 std::condition_variablecondition_variable_any 能在任何满足可基本锁定 (BasicLockable) 要求的锁上工作。

七,atomic

  • atomic是⼀个模板的实例化和全特化均定义的原⼦类型,他可以保证对⼀个原⼦对象的操作是线程安全的
  • 对于整形和指针⽀持基本加减运算和位运算

这里就不做详细介绍了,可以看这篇文章

八,综合练习

1. 交替打印奇数和偶数

主要使用条件变量

2. 实现生产消费模型

用于管理进各种信息


🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
不会代码的小猴2 小时前
C++的第十五天笔记
数据结构·c++·笔记
tbRNA2 小时前
C++基础知识点(六)类和对象
开发语言·c++
小此方2 小时前
Re:从零开始学C++(二)基础精讲·下篇:内联函数与空指针
开发语言·c++
草莓熊Lotso3 小时前
C++11 核心特性实战:列表初始化 + 右值引用与移动语义(附完整代码)
java·服务器·开发语言·汇编·c++·人工智能·经验分享
初夏睡觉4 小时前
从0开始c++,但是重置版,第1篇(c++基本框架)
开发语言·c++
草莓熊Lotso4 小时前
GCC/G++ 编译器完全指南:从编译流程到进阶用法(附实操案例)
linux·运维·服务器·网络·c++·人工智能·自动化
cooldream200912 小时前
当代 C++ 的三大技术支柱:资源管理、泛型编程与模块化体系的成熟演进
开发语言·c++
Queenie_Charlie13 小时前
数字去重(set)
数据结构·c++·set
Ayanami_Reii13 小时前
区间不同数的个数-树状数组/线段树/莫队/主席树
数据结构·c++·算法·线段树·树状数组·主席树·莫队