📝前言:
这篇文章我们来讲讲C++11并发库中的常用对象和接口。
适合有Linux线程基础的人观看,主要简述一下C++并发库中的各种接口的用法。对于常用的东西写多了自然记住了,对于不常用的在使用的时候再去查文档就可以了。
🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏,Linux
文章目录
- 一、thread库
- 二、this_thread命名空间
- 三,future
-
- [1. std::future<T>](#1. std::future<T>)
- 四、mutex
-
- [1. 基本介绍](#1. 基本介绍)
- [2. 其他mutex](#2. 其他mutex)
- [3. lock 和 try_lock函数](#3. lock 和 try_lock函数)
- [五、lock_guard 和 unique_lock](#五、lock_guard 和 unique_lock)
- 六,condition_variable
- 七,atomic
- 八,综合练习
-
- [1. 交替打印奇数和偶数](#1. 交替打印奇数和偶数)
- [2. 实现生产消费模型](#2. 实现生产消费模型)
一、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;
- 创建空线程对象
thread(可调用对象,参数)创建线程- 不支持拷贝构造
- 支持移动构造(比如把匿名线程对象移动构造给已存在的空的线程对象)
还有一些其他接口,如 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命名空间
主要有四个函数(文档讲的很清楚)
get_id():用于在执行流里面获得当前执行流(线程对象)的ID(因为在执行流里面没有线程对象,无法直接调对线里面的get_id方法)yield:用于把线程挂起,放在同优先级线程的队列的队尾sleep_until:Blocks the calling thread until abs_time(阻塞到绝对时间)sleep_for:Blocks 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():等待指定时间,返回操作状态(ready、timeou、deferred)。valid():检查 future 是否有效(是否关联了异步操作)。
四、mutex
1. 基本介绍
mutex:C++ 中封装的互斥锁的类,接口lock和unlock不说了,其他接口文档讲的也很清楚(和Linux的pthread锁没什么区别)try_lock:申请锁失败后返回false(不阻塞),所以如果后面代码是临界区代码则可能执行有安全风险
传锁问题:
- 如果我们有一把局部锁(在主线程中),想要通过
thread的构造函数(通过传引用)传给从线程是不行的。 - 因为
thread(&mutex)的时候,是把mutex传给了thread的拷贝构造 - 但是,
thread底层封装的是pthread_create,pthread_create接收的是void*类型的参数 - 所以传入的
mutex以及其他参数会被打包成一个struct(这个过程中发生了拷贝),导致不是原来的锁了
解决方法:使用ref()来传参 或者 直接使用lambda来捕抓mutex
2. 其他mutex
timed_mutex:主要是提供try_lock_for/untiltry_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的方式呢?我们可以回忆一下智能指针,智能指针可以预防在malloc和free的时候出现异常,导致出现异常安全(就是因为异常malloc的空间没办法释放了)的情况。而lock_guard可以解决,lock和unlock因为异常安全无法unlock的情况。
六,condition_variable
condition_variable需要配合互斥锁系列进⾏使⽤,主要提供wait和notify接口wait需要传递⼀个unique_lock<mutex>类型的互斥锁,wait会阻塞当前线程直到被notify- 进入
wait的时候会自动释放锁 - 重新被唤醒的时候会自动申请锁
- 进入
notify_one唤醒在当前条件变量上等的一个,notify_all唤醒在该条件变量上的全部condition_variable_any类是std::condition_variable的泛化。- 相对于只在
std::unique_lock<std::mutex>上工作的std::condition_variable,condition_variable_any能在任何满足可基本锁定 (BasicLockable) 要求的锁上工作。
- 相对于只在
七,atomic
atomic是⼀个模板的实例化和全特化均定义的原⼦类型,他可以保证对⼀个原⼦对象的操作是线程安全的- 对于整形和指针⽀持基本加减运算和位运算
这里就不做详细介绍了,可以看这篇文章
八,综合练习
1. 交替打印奇数和偶数
主要使用条件变量
2. 实现生产消费模型
用于管理进各种信息
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!