870920 Menu

JUCE类库ThreadPool(线程池)详解

临时创建并结束大量的线程会导致性能下降,此时可使用预先分配线程的线程池技术,需两个类:

  • ThreadPool 线程池类。独立类,无父类。该类无虚函数,意味着无需派生子类,可直接使用
  • ThreadPoolJob 工作对象类。抽象基类,无父类。

纯虚函数:runJob(),该函数返回JobStatus枚举值,用来表明是结束本次运行,还是重复执行本函数

为实现程序代码的并发执行,可基于Thread类的派生来实现此功能。Thread对象有三重职责(创建线程,在所创建的线程上执行操作处理,结束线程),即:每次要执行并发式的操作处理时,均需为该处理分配一个新线程。或者说,每次分配一个新线程,均需执行具体的操作处理(当然也可开出空线程,不执行。不过这种用法没什么意义)。这种并发编程的处理思路虽然很简单和实用,但略显生硬,不够灵活。由于往往在run()函数中使用作用域锁定(临界区对象加锁),多少会造成资源的浪费和CPU执行周期的空转。

现在考虑一下这种想法:并发式执行多个操作处理,假设每个操作处理都是一个对象,这些对象既不占用主线程,又无需自己开辟新线程,同时还能运行于主线程之外的线程上,而且还可以反复执行,那么灵活性将大大增加,也更加节约资源。或者说,先一次性分配既定数量的线程,这些线程什么也不做,直接置于一个“池”中。而后,这些池中的空线程由一到多个(无法判断究竟有几个)并发式操作处理来轮流取用,谁需要,谁就临时使用池中的某条空线程来执行自己的操作处理,执行完毕,及时将临时使用的线程置为空闲状态,还给线程池。池中空线程的调度、分配和回收由系统内部自动管理,程序员无需关注更多细节。

(网站发表,具体解释略……)

明白了原理和思路,实现起来就简单了。只需使用ThreadPool线程池和ThreadPoolJob工作对象这两个类即可。ThreadPool相当于车棚,它负责开辟既定数量的线程,并负责通知快递员干活和还车。而ThreadPoolJob则相当于快递员及其所需完成的任务。ThreadPoolJob不必为自己开辟新线程,也无需主动要车干活,干完归还。公司有活时,车棚会自动通知,即ThreadPool对象addJob()。至于快递员是否已完成任务,车棚不管,需要快递员归还电动车时(工作对象归还线程),车棚就强制收回,即ThreadPool对象调用removeJob()。

具体流程:
 ThreadPoolJob类是抽象基类,派生类需实现其纯虚函数runJob(),在此函数中执行所需完成的具体任务。与使用多线程的原则与初衷相一致,该函数中往往是需要循环执行的代码。

 ThreadPool则是具体的功能类,它负责给出程序所需的线程数量,并负责添加和移除具体的ThreadPoolJob对象。添加时,可设置是否管理其生命期,即是否由本类自行销毁ThreadPoolJob类的堆对象。

 内容组件类将这两者聚合到一起,即:内容组件类中只需声明ThreadPool类的栈对象,构造函数中初始化该对象时,给出需创建的线程数量。

 内容组件类的功能性函数中,可临时创建一到多个ThreadPoolJob类的堆对象,创建后,ThreadPool对象调用addJob()函数添加之。添加后,ThreadPoolJob类的runJob()函数自动开始运行,并且自动运行在ThreadPool所提供的后台线程上。

 内容组件类中可设置判断条件或真假标志值,以此作为ThreadPool移除ThreadPoolJob对象的条件。移除后,ThreadpoolJob对象停止运行runJob(),所用的线程由ThreadPool自行收回。

三步实现线程池的示例代码:(网站发表,代码略)。