文章目录
1. 线程thread
1.1 thread构造函数参数
不支持拷贝构造函数,支持移动构造函数;拷贝构造函数被禁用,如果使用move(x)
函数会调用x
移动构造函数,之后x
不会指向任何的函数对象。
thread对象的初始化:
- 传入不带参的函数对象;
- 传入带参数的函数对象;
- 传入带引用参数的函数对象;
- 传入类的成员函数对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。线程对象退出时,析构函数中会去检测如果线程没有被join或者设置为detached,会抛异常。
thread提供的API接口:this_thread - C++ Reference (cplusplus.com)
2. 互斥锁mutex
2.1 mutex
构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。提供以下API接口:
lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
unlock(), 解锁,释放对互斥量的所有权。
try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
2.2 unique_lock 与 lock_guard
使用RAII(通过类的构造析构)技术:构造函数中调用lock(),析构函数调用unlock()。unique_lock 与 lock_guard的区别如下:
- unique_lock与lock_guard都能实现自动加锁和解锁,但是前者更加灵活,能实现更多的功能。
- unique_lock可以进行临时解锁和再上锁,如在构造对象之后使用lck.unlock()就可以进行解锁,lck.lock()进行上锁,而不必等到析构时自动解锁。
- lock_guard的效率更高,当不需要进行加锁解锁时,一般使用lock_guard。
使用场景:需要结合notify+wait的场景使用unique_lock; 如果只是单纯的互斥使用lock_guard。
3. 条件变量 condition_variable
3.1 condition_variable
互斥量是多线程间同时访问某一共享变量时,保证变量可被安全访问的手段。但单靠互斥量无法实现线程的同步。线程同步是指线程间需要按照预定的先后次序顺序进行的行为。
条件变量的使用过程:condition_variable提供的API接口:
- 拥有条件变量的线程获取互斥量;
- 循环检查某个条件,如果条件不满足则阻塞直到条件满足;如果条件满足则向下执行;
- 某个线程满足条件执行完之后调用notify_one或notify_all唤醒一个或者所有等待线程。
4. 原子变量 atomic
具体参考:atomic提供的API
5. 异步操作
std::future : 异步指向某个任务,然后通过future特性去获取任务函数的返回结果。
std::aysnc:异步运行某个任务函数
std::packaged_task:将任务和feature绑定在一起的模板,是一种封装对任务的封装。
std::promise:提供了一种设置值的方式,它可以在这之后通过相关联的std::future对象进行读取。
6. function与bind 用法
在设计回调函数的时候,无可避免地会接触到可回调对象。在C++11中,提供了std::function和std::bind两个方法来对可回调对象进行统一和封装。C++语言中有几种可调用对象:函数、函数指针、 lambda表达式、 bind创建的对象以及重载了函数调用运算符的类。和其他对象一样,可调用对象也有类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定。
function绑定函数对象的使用方法:
//1. 保存普通函数
std::function<void(int a)> func;
//2. 保存lambda表达式
std::function<void()> func_1 = [](