linux下线程池原理及实现
诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。构建服务器应用程序的一个过于简单的模型是:每当一个
什么是线程池? 诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创建一个新的服务对象,然后在新的服务对象中为请求服务。但当有大量请求并发访问时,服务器不断的创建和销毁对象的开销很大。所以提高服务器效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这样就引入了“池”的概念,“池”的概念使得人们可以定制一定量的资源,然后对这些资源进行复用,而不是频繁的创建和销毁。 线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。 简单线程池的实现 1、线程池,顾名思义,就是要创建很多线程。创建线程的函数pthread_create()应该是最容易被想到的。有线程创建就要有线程退出pthread_exit(),在线程退出前,如果线程没有设置pthread_detach()属性,那么显然要回收线程资源pthread_join()。当然咯,可能要获取线程的ID值pthread_self()。 2、第一步创建了线程线程池linux,刚开始线程是不做事情的,初始化好了,就等待。等待当然不会是while(1)这种函数,因为那样太消耗CPU资源。容易想到的等待自然是使用条件变量的等待pthread_cond_wait(),这个函数干两件事情,第一件是对解除与形参mutex对应的互斥锁,然后是重新加锁,为的是在线程将任务放入任务队列的一个缓冲。任务放入完成后,再加锁,这样不会影响其他任务获取加锁的权利。因此,在调用该函数之前,会自然会想到加互斥锁。初始化互斥锁函数pthread_mutex_init(),反初始化互斥锁函数pthread_mutex_destroy(),加锁函数pthread_mutex_lock(),解锁函数pthread_mutex_unlock(),稍微再细化一点,可能会用到尝试解锁pthread_mutex_trylock()。 3.、实现了上面二步,一个线程池的框架就初步搭起来了。当然没法用,因为真正干事情的线程全部在等待中,注意不应该是超时的等待pthread_cond_timewait()。要使处于阻塞状态的线程干事情,得用信号去唤醒它pthread_cond_signal(),“打鸟”的一个函数,开一枪,总会把这只鸟吵醒,但具体是那一只,看那只最先在那排队了(上面已经说了pthread_cond_wait()函数的等待队列问题)。当然也可以想到“打鸟惊群”的函数pthread_cond_broadcast(),打一枪,无论打没打着,一群鸟都飞走了。 4、有了上面的基础,接下来就重点关注任务部分了。当然线程数量有限,上面已经说了,是固定的数目。因此任务大于线程数时,排队是难免的了。因此创建了一个任务队列,队列中的每一项代表一个任务。任务队列的节点最简单的模型就是一个处理任务的回掉函数void* (*callback_function)(void *arg)。指向函数的指针,参数是个指针,返回值也是个指针。具体的函数和参数则需要另外写函数定义。没次调用完线程处理完这个任务,就需要把它从任务队列中删除。进入任务队列的任务数也不能无限多,因此也设为一个比线程数稍微大个几个的一个固定值。 5、线程动态创建:一个线程退出后(在任务执行失败时,就有可能会退出),主线程要能检测到,然后动态创建一个新的线程,以维持线程池中线程总数不变。可以通过pthread_join()阻塞等待回收子线程资源,但是这就意味着主线程在阻塞状态下干不了其他工作,因此考虑使用线程信号,在子线程结束时,给主线程用pthread_kill()发送一个SIGUSR1信号,在主线程接收到此信号时,通过调用注册函数signal()或sigaction()函数注册的函数创建一个新的线程。
(编辑:92站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |