C++ Concurrency in Action [9] | CH09 Advanced Thread Management
目录
警告
本文最后更新于 2023-12-19,文中内容可能已过时。
线程池
- 线程池一般会用一个表示线程数的参数来初始化,内部需要一个队列来存储任务。下面是一个最简单的线程池实现
|
|
- 如果想让提交的任务带参数会麻烦很多
|
|
- 书上实现的线程池都在死循环中使用了 std::this_thread::yield 来转让时间片
|
|
- 这样做的问题是,如果线程池处于空闲状态,就会无限转让时间片,导致 CPU 使用率达 100%,下面是对书中的线程池的 CPU 使用率测试结果
- 对相同任务用之前实现的线程池的测试结果
- 这里还是把书上的内容列出来,下文均为书中内容
- 这个线程池只能执行无参数无返回值的函数,并且可能出现死锁,下面希望能执行无参数但有返回值的函数。为了得到返回值,就应该把函数传递给 std::packaged_task 再加入队列,并返回 std::packaged_task 中的 std::future。由于 std::packaged_task 是 move-only 类型,而 std::function 要求存储的函数实例可以拷贝构造,因此这里需要实现一个支持 move-only 类型的函数包裹类,即一个带 call 操作的类型擦除(type-erasure)类
|
|
- 用这个包裹类替代
std::function<void()>
|
|
- 往线程池添加任务会增加任务队列的竞争,lock-free 队列可以避免这点但存在乒乓缓存的问题。为此需要把任务队列拆分为线程独立的本地队列和全局队列,当线程队列无任务时就去全局队列取任务
|
|
- 这可以避免数据竞争,但如果任务分配不均,就会导致某个线程的本地队列中有很多任务,而其他线程无事可做,为此应该让没有工作的线程可以从其他线程获取任务
|
|
中断
- 可中断线程的简单实现
|
|
- 在函数中使用
|
|
- 更好的方式是用 std::condition_variable 来唤醒,而非在循环中持续运行
|
|
- 和 std::condition_variable 不同的是,std::condition_variable_any 可以使用不限于 std::unique_lock 的任何类型的锁,这意味着可以使用自定义的锁类型
|
|
- 对于其他阻塞调用(比如 mutex、future)的中断,一般也可以像对 std::condition_variable 一样设置超时时间,因为不访问内部 mutex 或 future 无法在未满足等待的条件时中断等待
|
|
- 从被中断的线程角度来看,中断就是一个
thread_interrupted
异常。因此检查出中断后,可以像异常一样对其进行处理
|
|
- 假如有一个桌面搜索程序,除了与用户交互,程序还需要监控文件系统的状态,以识别任何更改并更新其索引。为了避免影响 GUI 的响应性,这个处理通常会交给一个后台线程,后台线程需要运行于程序的整个生命周期。这样的程序通常只在机器关闭时退出,而在其他情况下关闭程序,就需要井然有序地关闭后台线程,一个关闭方式就是中断
|
|
Buy me a coffee~
支付宝
微信