线程池精华

1. 总体想法

  1. 线程池初始时创建任务队列、线程池,添加并启动子线程,提供接口给用户添加任务到任务队列
  2. 子线程只管从消息队列中取任务执行,将执行结果异步返回给用户
  3. 线程池销毁时等待所有子线程退出后才销毁

2. 难点

1. 如何接收用户指定的各种任务

传入线程的都是函数,而函数的不同主要是传入的参数、返回值不同,导致没法使用一个函数指针队列来接受全部类型的函数指针

  • submitTask提供用于向任务队列中添加任务的API,由用户调用,参数类型为Task智能指针
  • 使用多态 :用户使用派生类指定任务,任务队列元素为指向基类的指针,且基类中定义一个纯虚函数run(),让子线程拿到基类指针后运行
  • 但是如果用户传入的是临时对象,空间释放后再访问会导致未定义行为,所以要求用户传入shared智能指针,使得主线程出作用域后Task派生类对象依然不会被释放

2. 线程池如何添加任务并将任务队列的任务下发给线程

线程和线程池是两个对象,也就是解决,对象之间如何通信的问题

  • submitTask是生产者,用于向任务队列中添加任务
  • threadFunc是消费者,用于从任务队列中取出任务,注意这个函数是在子线程对象中调用,且需要访问线程池对象中的任务队列
  • 线程池通过将threadFunc传递给子线程,让其执行该函数(消费任务队列),从而实现任务下放(此时子线程在此函数中拥有资源线程池

3. 如何实现Any类

  • 接受任意类型:构造函数模板
  • 存储任意类型:多态 + 类模板
  • 返回指定类型:函数模板 + dynamic_cast

4. 用户如何获取任务执行后的结果(异步)

对于任务,主线程将任务到任务队列等待读取结果,子线程从任务队列中取出执行写入结果,读者-写者模型

  • 首先要有一个Result对象用于接受任意 Any结果
  • 子线程要能访问到该对象并写入结果后通知主线程读取,即在Task中包含Result对象指针
    • 指针赋值也是关键的,Task对象是用户提供并调用submitTask传入的,所以需要在这里面创建Result、将Task中的Result指针赋值为此Result,最后将该Result对象返回给用户调用地方
    • TaskResult一一对应 关系,两者有彼此的指针,注意Task周期比Result周期短,所以Result中使用shared_ptr,要注意避免循环引用
    • Result中的Task是为了将Result赋值给Task中对应指针
  • 主线程在读取结果时如果还没有产生,就需要阻塞等待,借助信号量来实现

目前未解决的问题是,Result是栈上对象,如果出作用域就直接析构,但是此时子线程正在写入,就会出现问题:解决办法是变为堆对象,且由智能指针管理,使得写入时不会被析构

5. 线程是如何归还到线程池

不存在归还,子线程一直运行,需要结束时退出循环即可,但是需要从thread_中删除自己

  • 如果是fixed模式,不需要归还,每个线程创建后就直接启动,死循环读取任务队列
  • cached需要动态调整线程池thread_,任务太多创建新线程加入线程池并启动线程,不需要多余线程时需要从线程池中去掉对应的Thread对象并关闭线程
    • 这里也存在读者-写者模型,临界资源是thread_,主线程和子线程都会对thread_进行写
    • 但是对于thread_的操作都是在taskQue_之下完成,本身就有锁,所以不需要增加多余的锁
    • 难点在于:在子线程中去删掉thread_对应的线程,thread_需要使用map,并将key传入子线程让其从线程池中删除自己

6. 线程池退出后如何结束所有子线程

线程池是主线程,这里涉及到进程间通信,读者写着模型:isPoolRunning以及threads_

  • threadPool在析构时改变isPoolRunning通知子线程,并阻塞等待子线程结束,感知到子线程全部结束
  • 子线程状态不唯一,可能正在执行任务、可能正在等待任务,但最终都会处于即将等待任务,此时可以读取到线程池要结束了,然后将自己对应的thread对象从线程池中清除

7. 优化输入输出

  1. 上述的输入是借助多态,但是得先定义类,输出是Result,但是目前看来存在多线程的问题
  2. 目标是submitTask接受函数指针和参数,返回一个结果对象,没有多线程问题
  1. submitTask接受函数,以及可变参--》可变模板
  2. 异步返回值--》packaged_task<>future<>
  3. 任务队列统一类型--》lambda中间件
  4. 函数返回类型借助传入参数确定--》auto、后置返回类型、decltype

3.从数据结构中看线程池

待补充

相关推荐
秋风&萧瑟1 小时前
【C++】C++中的友元函数和友元类
c++
梁诚斌1 小时前
使用OpenSSL接口读取pem编码格式文件中的证书
开发语言·c++
沃夫上校3 小时前
Feign调Post接口异常:Incomplete output stream
java·后端·微服务
LeeGe3 小时前
SpringAOP中@within和@annotation以及 @within和@target的区别
后端
一个平平无奇的Java小学生3 小时前
Spring Cloud Alibaba 微服务从入门到生产部署完整指南
后端
一个平平无奇的Java小学生3 小时前
Spring Cloud Alibaba 微服务实战指南
后端
张小洛3 小时前
Spring IOC容器核心阶段解密:★Bean实例化全流程深度剖析★
java·后端·spring·ioc容器·bean实例化
小王子10243 小时前
Django+DRF 实战:从异常捕获到自定义错误信息
后端·django·web开发
hdsoft_huge3 小时前
Spring Boot 高并发框架实现方案:数字城市的奇妙之旅
java·spring boot·后端
00后程序员4 小时前
WebView 无法调用原生分享功能?调试复现与异常排查全过程
后端