面向task
任务编程, 而不是面向线程编程;
异步多线程的开发机制
std::thread
auto fut = std::async(doAsyncWork);
task
比thread
好
get
返回值, 就像调用普通函数一样; (返回值内存中存储);pthread_join
也支持返回值, 不过要写的代码更多;std::thread
没有办法获取;get
抛出异常, 可以在get
捕获另一个线程堆栈抛出的异常;pthread_join
也可;std::thread
崩溃死亡, 进程也可能因此死亡;- 更加高层的抽象; 告别线程管理;
线程知识
- 物理线程: 即核心数量, 超线程则多一个或多个硬件线程;
- 软件线程: 即由操作系统抽象出来的; 轮流执行; 可多余硬件线程;
std::thread
: 将软件线程handle
封装; 更好的管理生命周期;
线程管理
资源超限导致创建失败
- 一般由于线程数查过系统限制;
ulimit -u 100
再创建1024
个线程就会触发;
#include<thread>
#include<vector>
int main() {
std::vector<std::thread> a;
for(int i = 0 ; i < 1024; i++)
a.emplace_back([](){while(1);});
}
执行结果
ch@ch-ubuntu:~/ch/cppfile/max_thread$ ulimit -u 100
ch@ch-ubuntu:~/ch/cppfile/max_thread$ ./a.out
terminate called after throwing an instance of 'std::system_error'
what(): Resource temporarily unavailable
Aborted (core dumped)
ch@ch-ubuntu:~/ch/cppfile/max_thread$ cat test.cpp
#include<thread>
#include<vector>
int main() {
std::vector<std::thread> a;
for(int i = 0 ; i < 1024; i++)
a.emplace_back([](){while(1);});
}
规避超限方案一: 串行执行
问题是: 如果串行线程是UI
线程, 会导致长期得不到响应而退出;
规避超限方案二: 等待线程销毁
但是如果所有线程都在等待这个即将新建的线程执行结果呢? (死锁)
比如: 条件变量之类的;
没有超限时可能存在的问题: 上下文切换
原因: 应用程序可执行数量超过硬件线程(核心)
;
cache miss
: 即新核心缓存没有多少指令或数据在这个核心的cache
中;
cache pollute
: 新线程污染了cache
, 之前的线程又切换到当前核心, 之前的都被污染了;
差不多一个意思;
如何规避频繁上下文切换
影响管理策略: 和调度, 线程变化负载, 切换一次开销, cpu cache
, 跨平台适用性;
实在太麻烦了, 交给专业的C++
标准库负责人来开发; 用std::async
;
减少资源超限异常;
减少上下文切换;
std::aysnc
不保证创建线程; 可能串行; 也可能并行; 即在future.get
代码所在线程执行; 就像是a
线程设置,b
读取, 但是函数是在b
执行的;
async
不是万能的
async
可以从系统的角度决策负载, 但是无法确定你的流程逻辑;
如果get
是UI
线程, 同样会导致长期无法响应; 所以还是需要开发者在std
管理负载的基础上自行修改逻辑;
std::launch::async
强制创建线程;
线程池和负载均衡
前沿技术: 目前一些流行的线程池项目: 用系统级别线程池避免线程超限; 使用workstealing algorithms
来达到负载均衡;
标准库: C++
11因为很多并发标准,使得上面的很难实现; 但是有的标准库提供者实现了(gnu, clang, others)
;
std::async
: 如果你用了, 可能会用到最前沿的技术; 如果没用, 也和std::thread
持平; 反正不亏;
std::thread
: 自行管理, 不一定有人家做得好; 而且花很多时间; 而且需要高度定制; 建议自行尝试, 不用总是拿来主义, 小心被卡脖子;
对比
task based
- 不用线程管理;
- 有返回值;
std::thread
- 更加精细的控制; 获取底层
API
, 进行更加定制化的操作; 优先级, 堆栈, 等等属性; - 特定环境调优: 比如根据某个操作系统定制化其特定的线程调度策略进行调优;
- 线程池: 前面说了有的
std::async
底层没用线程池; 这时候就需要自己实现;