c++并发
c++并发编程实战笔记
基本介绍
为什么使用并发
· 为了分离关注点(多并发走的是多套不同逻辑,容易排查问题所在)
· 为了性能
什么时候不能使用并发
增益小于成本时
·分离关注点效果不明显,反而使代码复杂难懂
· 性能提升不大
· 系统资源有限时
线程管理
启动新线程
注意:
重载运算符()时
thread my_thread(mufunc());
这一句会被当成函数定义,因为mufunc()返回一个mufunc对象,会被认为是定义变量mufunc variable这样,所以整句就成函数定义了
所以此时更改语法
thread my_thread{mufunc()};就会被认为是一个参数了,而非类型定义
或者
thread my_thread((mufunc()));
或者直接用lambda函数都可以避免语法歧义
等待与分离线程
mythread.detach//不等待进程结束直接运行
如果在进程没有执行完之前开启线程的进程已经没了。那么进程在调用函数的时候就会找不到定义
等待就是阻塞,分离就是非阻塞
线程唯一标识符
std::thread->get_id()
线程间共享数据
共享数据带啦来的问题
竞争
1.互斥量保护数据(std::mutex)
std::lock_guardstd::mutex上锁,同锁只时仅一个可以修改。
2.死锁
两个进程各自占有一个资源(需要两个),导致死锁
· 以相同顺序上锁。比如需要ab,则两个进程都先锁a再锁b,那么不会出现a被锁,b被另一个进程锁的情况
· std::lock同时锁两个
· 层次锁,设置分层避免死锁
3.灵活的锁
unique_lock,传入第二个参数控制互斥量
4.所有权传递
锁的互斥量所有权可以通过继续上锁传递给新的锁
5.锁的粒度
锁数据量的大小
6.保护数据初始化(延迟初始化需要等待)
call_once(),once_flag()使指针只初始化一次
7.保护数据不常更新的数据结构
std::shared_mutex,std::shared_timed_mutex
允许一个修改,多个读取。修改时阻止读取
嵌套锁
std::recursive_mutex,对同一个互斥量上多次锁(一个实例多个锁)。上多少次就得释放多少次,否则获取不了
同步共享操作
等待事件或条件
等待条件达成
std::condition_variable和std::condition_variable_any
前者为首选,后者条件宽松
std::condition_variable a()
a.awit(锁,call_back)
另一边需要用a.notify_one()或notify.all()进行通知
条件变量构建线程安全队列
线程安全队列加入条件变量控制
使用期望值等待一次性事件
周期性检查期望值是否达成,然后进入等待
后台任务的返回值
std::async
任务与期望值关联
把任务达成的返回值作为期望。
使用promises
通过和future配合,可以提前使用期望值
uniqe_lock详解
1.uniqe_lock取代lock_guard
unique_lock可以省去解锁操作
2.uniqe_lock的第二个参数
1.adopt_lock表示互斥量已经被lock了(需要把互斥量提前lock)
2.try_to_lock尝试加锁,如果互斥量没有加锁成功,也会返回,不会阻塞(前提是自己不能先lock互斥量)
3.defer_lock 初始化一个没有加锁的互斥量,方便随时开锁关锁,也会自动解锁(不能先lock互斥量)
3.uniqe_lock的成员函数
1.lock
2.unlock
3.try_lock
4.release
4.uniqe_lock的所有权传递
uniqe_lock可以把自己互斥量的所有权转移,所有权只能转移,不能复制
std::uniqe_lockstd::mutex my_unique2(std::move(my_unique1))//利用移动构造函数
单例设计模式共享数据分析、解决,call_once
单例设计模式共享数据分析、解决
std::mutex resource_mutex
class Singleton
{
private:
static Singleton* instance;
private:
Singleton() {};
~Singleton() {};
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton* getInstance()
{
//优化效率
if(instance == NULL){
// 多线程需要加入互斥量,避免创建多个对象
std::unique_lockstd::mutex my_mutex(resource_mutex)
if(instance == NULL)
instance = new Singleton();
return instance;
}
}
};
// init static member
Singleton* Singleton::instance = NULL;
call_once
保证多线程情况下某函数只调用一次
可以用call_once 代替上面的多线程单例实现
condition_variable、wait、notify_one、notify_all
condition_variable
等待条件达成
生成一个对象condition_variable obj
wait
obj.wait()
pagram1:互斥量,解锁互斥量并堵塞,直至notify_one之后才会尝试获取互斥量并上锁
pagram2:条件
notify_one
唤醒wait,wait开始尝试获取互斥量上锁并判断条件
不一定能唤醒,因为可能线程没有阻塞在wait位置
notify_all
同时唤醒所有线程
async、future、packaged_task、promise
async、future创建后台任务并返回值
创建异步任务返回futrue对象(异步执行完成结果),但是获取值时会阻塞在获取的位置等待线程执行结束
packaged_task
将对象包装起来,后续可以通过get_futrue获取值(不用async也可以让普通线程获取futrue对象)
promise
也可以和set_value之后通过get_futrue拿到futrue对象
future其他成员函数、shared_future、atomic
shared_future
一个线程结果需要使用多次时使用,原本futrue.get()是转移数据,无法使用多次
atomic
最基础的操作,不可分割(不可出现待完成状态,只有执行前和执行后两个状态),在这个操作过程中不可被线程打断,类似于上锁,但是效率比上锁高,只能对一个对象进行原子操作
std::atomic续谈、std::async深入谈
std::atomic续谈
有些不支持原子操作,比如g = g+1
其实这里可以理解为是两步执行,所以原子操作失效
std::async深入谈
1.async参数详述
- launch::deffered[延迟调用,不创建新线程],launch::async[强制异步任务在新线程执行]
- std::thread创建有可能失败,比如线程数量达到上限
- std::async有时不创建新线程(launch::deffered时不创建新线程)
- std::async额外参数若是 launch::deffered|launch::async则有可能是任意一个,系统自己决定
- 若不传额外参数,则默认为 launch::deffered|launch::async
- 自行决定规则:若可以创建新线程,则launch::async,若不能创建新线程,则 launch::deffered
2.async和thread区别
3.async不确定性问题解决
windows临界区、其他各种mutex互斥量
windows临界区
类似互斥量,需要定义一个临界区,若临界区被定义就执行临界区代码,否则执行c++代码
多次进入临界区
多次进临界区不会报错,而多次lock会异常
自动析构
临界区可以封装成类在初始化时加载临界区,析构时释放临界区,自动释放资源(类似于lock_guard)
recursive_mutex递归的独占互斥量
允许同一个线程中的某互斥量多次lock
带超时功能的互斥量
1.timed_mutex,超时不会继续阻塞
try_lock_for(time):在时间内尝试拿锁,如果拿到了返回true,没拿到返回false,到了时间或者加锁成功不会阻塞
try_lock_until():参数是一个未来的时间点,到了时间或者加锁成功不会阻塞
2.recursive_time_mutex,递归超时不会继续阻塞,并使一个线程可以多次获取互斥量
补充
虚假唤醒
wait,notify_one, notify_all
唤醒时,数据不符合条件,一般是由于读数据次数与取数据次数不匹配导致的
atomic
cout<<atomic<<endl//不是原子操作,本质也是多次操作(有很多类似的也不是原子操作)
原子操作对象不可寻常的拷贝复制(=),可以atomic ato1(ato)
浅谈线程池
场景设想
线程过多怎么办?
线程创建失败怎么办?
使用线程池优化,避免重复创建消耗资源
实现方式
与传统对象池类似,在开始时创建并保存,避免临时创建
线程创建数量谈
1.数量极限问题
一般是2000个,多了就崩溃
2.数量建议
api接口提供商,一般会有建议数量,可以多参考
更多推荐
所有评论(0)