最近因工作问题,需要实现一个简单的线程池,满足一下要求,
-
可伸缩,即一旦发现线程不够用,则可以动态增加线程。(至于缩减线程,这个可能难度比较大,暂时不考虑)。
-
支持超时任务。比如提交一个Task,可以设置5秒后执行,并且可以设置执行一次,还是每隔5秒执行。
基本上就是这两个要求。下面讨论下实现上的问题。
1 Windows平台实现一个线程池(不使用完成端口),我最初的想法是:
-
创建比如20个线程,然后这20个线程都WaitForXXXObject,在一个Event上。
-
添加任务的时候,触发这个Event,为了避免惊群现象(例如只有1个任务的时候,20个线程全起来抢任务,结果其他19个线程白起来了。这样导致系统效率会显著下降。),CreateEvent的时候可以设置下,保证只有一个在等待的线程会启动。
-
不太好实现超时任务,这20个线程,该哪一个去做超时检测呢?虽然Wait函数提供了超时机制,但总不可能这20个都去做超时检测吧?
-
解决办法是,可能需要单独一个线程去定时检查,然后把到期的任务加入到任务队列头,然后触发Event,这样其他线程就能顺利得到任务了。
-
另外一种方式是这20个线程中有一个做超时检查,然后执行最先到期的任务。这样的问题就是如果该线程执行的任务要是时间过长的话,将导致其他超时任务没法执行。
-
综合考虑的话,还是用一个单独的线程去检查比较好。
2 Windows平台上使用完成端口
-
使用完成端口就简单了,线程池中的线程个数将是CPU个数的2倍(这个是推荐值,应该也是绝大多数人的选择了)。
-
Wait的时候,当有事情发生时,内核会自动调度一个线程去执行。而且接连的任务会倾向于使用同一个线程来执行。这样可以减少线程切换的时间。所以,惊群效应由内核保证不会发生了。
-
解决超时任务的检查,这个也没有更好的办法。只能采取和上面一样的方法了。
三 Linux平台
1 Linux平台实现线程池,基本思路和Windows一样,但是由于Linux提供的线程同步原语(POSIX)不如Windows好用,导致有一些特殊的地方需要注意:
-
在Windows上,线程间通知无外乎就是使用Event,在Linux上,使用POSIX的condition+mutex,也能完成同样的操作,但是这种方式在linux中用得相对较少,而是大量使用pipe,创建两个fd。当线程1想唤醒线程2的时候,就可以往writeFD中写数据,这样线程2阻塞在readFD中就能返回。我之前及其没搞明白为何要使用pipe,后来突然想明白了。因为Linux上阻塞的方法就是用select,poll和epoll,其中等待的都是FD,那么采用FD这种方式,能够统一调用方法。(因为POSIX缺乏WaitForMultiObject导致,网上有一个Linux下Wait/CreateEvent的系列实现,很难下载到。呵呵)
-
但是,现在有20个线程,总不可能创建20个pipe吧?即使有20个pipe,那么我要唤醒哪个线程,就得单独往那个线程的writeFD写数据。这明显不是一个好办法。只能使用condition+mutex了。为了避免惊群,操作cond的时候,只能使用Signal,而非Broadcast
-
超时任务的处理,那么也只能和Windows一样的方法了
四 一个实际的线程池设计
-
对用户来说,线程池处理的是任务。所以给线程池添加的应该是任务。那么任务有高,中,低三个优先级。另外,有些任务是需要一直占有线程的,所以还区分persist和非persist两种。设计的时候,分别有高中低三个优先级队列。线程池在挑选任务的时候,先处理高,然后是中低优先级的任务。
-
当线程池中的线程个数小于任务个数的时候,线程池应该自动增加池中的线程。这个就是自动扩展。当线程池中的线程检测到自己长时间没有工作,并且池中线程数量超过最大设置的数量时候,那么线程应该自动退出。按这种方式就可以实现一个比较好的自动缩减。这样的话,每个线程都需要有一个超时检测,看看自己是不是多余了。
-
对于定/超时任务,需要给线程池增加一个高优先级,persist的job。这个job自带一个定时任务队列。添加定时任务,其实就是给这个队列添加成员。然后唤醒该线程。这个线程从其中选择一个任务,然后再提交给线程池。(其实就是我们在Windows上的方案)
一 缘由: 最近因工作问题,需要实现一个简单的线程池,满足一下要求,可伸缩,即一旦发现线程不够用,则可以动态增加线程。(至于缩减线程,这个可能难度比较大,暂时不考虑)。支持超时任务。比如提交一个Task,可以设置5秒后执行,并且可以设置执行一次,还是每隔5秒执行。基本上就是这两个要求。下面讨论下实现上的问题。 二 Windows平台1 Windows平台实现一个线程池(不使用完成端口),我最
1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率。2.线程并发数量过多,抢占系统资源从而导致阻塞。3.对线程进行一些简单的管理。1.定义
一个
基本函数类,用户可继承此类
实现
自定义方法即可使用。
2.定义
一个
函数指针,为基本类的函数对象。
3.定义
一个
仿函数类
实现
,重写“()”运算符。
4.自定义线程类
5.定义
线程池
类
6.
实现
继承基础类。
7.使用
线程池
类。......
在学习使用stl容器的时候,我们见到了stl容器使用了内存池来进行时间优化,因为用户在使用new、malloc等向系统申请空间的时候,身份会发生变化,同时有可能要执行操作系统内的内存处理算法,这可能会比较耗时。于是使用了内存池来提前申请好大块内存空间,需要用户自行进行管理。与内存池类似,提前准备好的线程,用来随时处理
任务
,这就被称作
线程池
。
线程池
是线程的一种使用模式。线程过多会带来调度开销,从而影响缓存局部性和整体性能。而
线程池
中维护着多个线程,等待分配可并发执行的
任务
。这就避免了在处理短时间
任务
时创建和
我想只要是写过或者想要写C/S模式网络服务器端的朋友,都应该或多或少的听过完成端口的大名吧,完成端口会充分利用
Windows
内核来进行I/O的调度,是用于C/S通信模式中性能最好的网络通信模型,没有之一;甚至连和它性能接近的通信模型都没有。
完成端口和其他网络通信方式最大的区别在哪里呢?
(1) 首先,如果使用“同步”的方式来通信的话,这里说的同步的方式就是说所有的操作都在
一个
线程内顺序执行完成,这么做缺点是很明显的:因为同步的通信操作会阻塞住来自同
一个
线程的任何其他操作,只有
大家好,从今天开始,我将定期写一些知识分享,把自己在前后端学习过程中觉得有用的知识点分享出来,大家可以关注学习,也欢迎大家和我一起讨论,有问题也可以问我,一定耐心解答,我的想法就是帮助别人不要再走自己踩过的坑。
代码视频链接: 戳这里瞅一瞅.
文章目录为什么是
线程池
同步的方式更高效的做法更优秀的方案干掉锁把
线程池
的尺寸
为什么是
线程池
先思考一下,在哪些场景下会用到多线程:
阻塞调用(阻塞IO、等待资源、耗时操作)
耗时的计算(复杂的计算或者继续请求等操作)
高密度的
任务
(高并发,低延时场景)
总之就是需
1、newCachedThreadPool
创建
一个
线程池
,如果
线程池
中的线程数量过大,它可以有效的回收多余的线程,如果线程数不足,那么它可以创建新的线程。
public class CacheThreadPoolTest {
private static int counter = 0;
public static void main(String[] args) throws Exception {
ThreadPoolExecutor threadPool = (Thr
jxcyly1985:
神农班和我的2020年总结
GOLDTAI:
深入理解Android之Xposed详解
也无风雨也无晴191: