《C++并发编程实战》
第二章. 管理线程¶
c++11起有了新的线程感知内存模型(thread-aware memory model),支持多线程,并通过std::thread
等类对多线程进行管理。
这里根据书中第二章内容,先简单介绍std::thread
类如何使用。
1. std::thread¶
头文件
#include <thread>
C++程序main函数启动时,本身就是一个线程,在这个线程中创建std::thread
对象(构造时需要传入线程运行的函数),就是启动了另一个线程。
std::thread
就是一个对象,只是可以和一个运行中的线程相绑定,持有std::thread
对象的线程可以和其绑定的线程进行一定的交互。
1.1 方法列表¶
成员方法
- 构造函数
- 析构函数:调用析构时不能还绑定着运行中的线程(析构前就调用join
或detach
)
- 操作符=:只支持移动场景,不支持拷贝
Observers
- joinable:判断对象是否绑定着运行中线程,实现是判断get_id() == std::thread::id
- get_id:获取绑定中线程的id,如果没有绑定线程,则返回当前线程id,即std::thread::id
- native_handle:获取底层handle
- hardware_concurrency:当前系统硬件并行度,例如双核+超线程,结果返回4
TODO: 对get_id
做进一步说明
Operators
- join:只能在joinable
为true
时调用,等待绑定中线程运行完成并返回结果,之后和该线程解除绑定(即joinable
返回false
)
- detach:只能在joinable
为true
时调用,和解除和线程的绑定关系,此后程序中无法再操作这个线程,也就是把这个线程作为守候线程(daemon thread)运行
- swap:交换两个std::thread
对象
1.2 构造¶
- 可以构造一个不绑定方法的
std::thread
,即不传任何参数,此时std::thread
不绑定任何运行中线程 - 传入函数,即新起一个线程,并且把这个线程绑定在
std::thread
对象上 - 不支持拷贝构造
- 可以移动构造,即把另一个
std::thread
绑定的线程转移到这个std::thread
对象上,绑定的线程本身运行不受影响
构造std::thread
,可以用正常Function和Args,可以传lambda,可以传重载了operator ()
的类。
TODO: 写下几种构造的demo
1.3 析构¶
如果std::thread
对象在销毁前,没有决定被join
或detach
,std::thread
的析构函数中会调用std::terminate
。
所以std::thread
对象销毁前,必须显式调用join
或detach
,否则在std::thread
的析构函数中会调用std::terminate
,中止整个程序。
例如如下代码
#include <iostream>
#include <thread>
int main() {
auto func = []() {
std::cout << "Hello World in another thread\n";
};
std::cout << "Begins\n";
{
std::thread f(func);
}
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "Ends\n";
}
因为f
对象没有显式处理,程序运行结果如下,没有输出"Ends"
Begins
libc++abi: terminating
zsh: abort ./a.out
2. 传递参数给线程函数¶
TODO: 说明参数传递过去后的作用域,看下什么时候参数会被copy,通过std::ref来指定传递引用,以及lambda能不能更方便实现传递引用(lambda里捕获引用的话)
TODO: 说明std::thread
不能直接获取线程结果,可以靠传一个引用进去来写入,也可以靠future
机制
2. 互斥锁std::mutex¶
c++11引入,线程安全,同时只能被一个线程持有
#include <mutex>
2.1 基本操作¶
- lock: 锁住该互斥量,如果被其它线程占有,则当前线程被阻塞,如果当前互斥量被当前线程锁住,则会产生死锁
- unlock: 释放对该互斥量的所有权
- try_lock: 尝试锁住mutex,如果失败则返回false,不会阻塞
2.2 std::lock_guard¶
c++11引入
对std::mutex
提供RAII(Resource Acquisition Is Initialization)形式的封装类
#include <mutex>
#include <thread>
#include <iostream>
int num;
std::mutex m;
void increment() {
// 进行加锁
const std::lock_guard<std::mutex> lock(m);
std::cout << "increment in thread " << std::this_thread::get_id() << ", add num to " << ++num << '\n';
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; i++) {
threads[i] = std::thread(increment);
}
for (int i = 0; i < 10; i++) {
threads[i].join();
}
std::cout << "num " << num << '\n';
}
2.3 std::unique_lock¶
c++11引入
2.4 std::scoped_lock¶
c++17引入