并行和并发

并行和并发

fetch150zy

并行和并发

并行基础

example

1
2
3
4
5
#include <thread>
int main() {
std::thread t([](){...});
t.join();
}

互斥量与临界区

std::mutex是C++11中最基本的mutex类,通过实例化std::mutex可以创建互斥量,而通过其成员函数lock()可以进行上锁,unlock()可以进行解锁。但在实际编写代码过程中,最好不去直接调用成员函数,因为调用函数就需要在每个临界区的出口处调用unlock()

RAII语法的模板类std::lock_guard,保证了代码的异常安全性

example

1
2
3
4
5
void critical_section(int change_v) {
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
// ...
}

这样无论函数是正常返回还是中途抛出异常,都会引发堆栈回退(自动调用了unlock

std::unique_lock则是相对std::lock_guard出现的,std::unique_lock更加灵活,会以独占所有权的方式管理mutex对象上的上锁和解锁的操作

tips: std::lock_guard不能显式的调用lock和unlock,而std::unique_lock可以在声明后的任意位置调用,可以缩小锁的范围,提供更高的并发度

如果用到了std::condition_variable::wait则必须使用std::unique_lock作为参数

example

1
2
3
4
5
6
7
8
9
void critical_section(int change_v) {
static std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
// ...
lock.unlock();
// 此期间任何人可以抢夺v的持有权
lock.lock();
// ...
}

期物

std::future提供了一个访问异步操作结果的途径

对于下面这个问题

主线程A希望开辟一个线程B去执行某个我们预期的任务,并返回我一个结果;此时线程A可能正在忙其他的事情,无暇顾及B的结果,我们会希望在某个特定时间获得线程B的结果

C++11的std::future被引入之前,通常的做法是:创建一个线程A,在线程A里启动任务B,当准备完毕后发送一个事件,并将结果保存在全局变量中。而主函数线程A里做其他的事情,当需要结果的时候,调用一个线程等待等待函数来获得执行的结果

example

1
2
3
4
std::packaged_pack<int()> task([](){...});	// 将函数封装到task中
std::future<int> result = task.get_future(); // 在一个线程中执行task
std::thread(std::move(task)).detach();
result.wait(); // 设置屏障

条件变量

条件变量std::condition_variable是为了解决死锁而生,当互斥操作不够用时而引入的

在一个忙等待循环中可能会导致所有其他线程都无法进入临界区使得条件为真时,就会发生死锁

原子操作与内存模型