C++多线程
一、 概念
1. 进程
可简单理解为exe的一次运行
2. 线程
一个进程可以有多个线程(但有且只有一个主线程)
二、 并发实现
1. 多进程
解决进程间通讯
2. 多线程
一个主线程多个子线程实现并发,进程资源在线程间共享
三、 C++线程创建
3.1 基本函数创建
1. 包含头文件
1 |
2. 创建线程
创建thread对象
1 | thread th1(func); |
- 阻塞主线程主线程等待子线程执行完毕
1
th1.join();
- 分离子线程将子线程与主线程分离,子线程与主线程不再关联
1
th1.detach();
- 每个线程只能做一次后续处理,不管是
join
还是detach
.
- 判断一个进程是否可以做后续处理
1
th1.joinable();
3.2 类和对象创建
重载括号运算符制作函数类,将函数类的对象作为线程参数3.3 Lambda表达式创建
将lambda表达式作为参数3.4 带参创建
传引用参数需要ref()
包装3.5 带智能指针
用move()
但是之后原智能指针销毁3.6 类成员函数创建
1
thread th1(成员函数指针,成员函数所属的对象,参数(可能需要包装))
四、异步
4.1 包含头文件
1
4.2 创建未来返回值对象
1
future<type> fu1;
4.3 异步运行
1
fu1=async(func/lambda)
1 | fu1.get() |
- 此时异步程序若仍未执行完,将阻塞程序至异步程序执行完并取回值
4.5 等待异步程序执行完毕但不需要返回结果
1
fu1.wait()
4.6 仅等待一段时间(超时检测)
1
fu1.wait_for(chrono时间单位())
- 如超时未执行完毕则返回
future_status::timeout
,按时执行完毕则返回future_status::ready
4.7 不异步仅延迟至get时运行
将async
第一个参数设为std::launch::deferred
4.8 手动管理线程
不使用async而是使用promise在传入的可执行体中设置未来值1
std::promise<type> pr1=promise(func/lambda)
pr1.set_value()
,然后在外面get_future
获取未来返回值对象,并进一步get获取未来值五、锁
1. 导入头文件
1
2. 创建锁对象
1
2mutex mtx1
timed_mutex tmtx1 //可以等待一会的锁3. 基本操作
1
2mtx1.lock() //加锁
mtx1.lock() //解锁 - 一个锁锁住就不能再锁,后面试图加锁的就会被阻塞至前一线程解锁
4. 自动解锁
1
lock_guard gl1(mtx1)
- 创建即加锁,离开作用域自动解锁
1 | unique_lock<std::mutex> ul1(mtx1) //创建即加锁 |
锁所有权转移要用
move()
5. 读写分离锁
1
2
3
4
5
6shared_mutex sml1 //创建锁
sml1.lock() //锁写
sml1.unlock() //解锁写
sml1.lock_shared() //读锁
sml1.unlock_shared() //解锁读
shared_lock sl1(shared_mutexd对象) //自动解锁(类似unique_lock)读锁可以锁多次,多个线程一起读,但读的时候不能写(计数)
多个对象建议每个对象一个锁
6. 死锁
- 一个线程不要同时持有多个锁
- 线程的上锁顺序一致
- 使用
lock(mtx1,mtx2,...)
对多个锁上锁可以自动处理上锁顺序确保不产生死锁 - 使用
scoped_lock sl1(mtx1,mtx2,...)
对多个锁上锁可以确保不产生死锁并自动解锁 - 单个线程死锁可以用
recursice_mutex
代替mutex
,每次加锁会加一个计数,计数0解锁,但是会有性能损失
六、条件变量
1. 导入头文件
1 |
2. 创建条件变量对象
1 | condition_variable cv1 |
2. 等待条件
1 | cv1.wait(锁,可以提供执行体如返回true才唤醒) //多个wait被唤醒时锁确保只有一个线程被运行,等待状态中锁会被暂时解锁 |
4. 发送唤醒信号(另一线程)
1 | cv1.notify_one() //唤醒一个wait的线程 |