Clamav杀毒软件源码分析笔记[六]
刺猬@http://blog.csdn.net/littlehedgehog
[线程处理]
我曾经说过Clamd强大的动力并不是来自于某些催情药的功用,而是内部提供的线程池功能功不可没.所谓线程池,我理解是暂时收容工作完成的线程,派发工作给无所事事的线程,这样可以让线程有暂时的不死之身,一直存在于内存之中.这样避免了频繁地创建线程,销毁线程.
先来看看所谓线程池的结构吧:
- typedef struct threadpool_tag
- {
- pthread_mutex_t pool_mutex;
- pthread_cond_t pool_cond;
- pthread_attr_t pool_attr;
-
- pool_state_t state;
- int thr_max;
- int thr_alive;
- int thr_idle;
- int idle_timeout;
-
- void (*handler)(void *);
-
- work_queue_t *queue;
- } threadpool_t;
这个结构普通至极,用旭哥的话来说那就是"那个简单──“.如果用C++来描述这个结构,或许改称呼"类",还应该有所谓的.....那个....成员函数, 可惜我C++实在不好,旁边有位C++高手,此时此刻正研究3D引擎,不便打扰.
线程池创建:
- threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void *))
- {
- threadpool_t *threadpool;
- if (max_threads <= 0)
- {
- return NULL;
- }
- threadpool = (threadpool_t *) mmalloc(sizeof(threadpool_t));
- threadpool->queue = work_queue_new();
- if (!threadpool->queue)
- {
- free(threadpool);
- return NULL;
- }
- threadpool->thr_max = max_threads;
- threadpool->thr_alive = 0;
- threadpool->thr_idle = 0;
- threadpool->idle_timeout = idle_timeout;
- threadpool->handler = handler;
-
-
- pthread_mutex_init(&(threadpool->pool_mutex), NULL);
- if (pthread_cond_init(&(threadpool->pool_cond), NULL) != 0)
- {
- free(threadpool);
- return NULL;
- }
- if (pthread_attr_init(&(threadpool->pool_attr)) != 0)
- {
- free(threadpool);
- return NULL;
- }
- if (pthread_attr_setdetachstate(&(threadpool->pool_attr), PTHREAD_CREATE_DETACHED) != 0)
- {
- free(threadpool);
- return NULL;
- }
- threadpool->state = POOL_VALID;
- return threadpool;
- }
线程池创建绝非难事,就是一系列结构成员复制而已,如果对线程了解不深,如我一般,可以看看我注释中提供的IBM资料
线程池销毁,我说"销毁"这个词显得太学究,如果说"干掉线程池"有太过于黑社会化. 销毁跟创建大致相反,我只是说了大致而已,注意,因为我们要考虑如果销毁线程的时候,还有线程运行的话,我们必须等待.
- void thrmgr_destroy(threadpool_t *threadpool)
- {
- if (!threadpool || (threadpool->state != POOL_VALID)) {
- return;
- }
- if (pthread_mutex_lock(&threadpool->pool_mutex) != 0) {
- logg("!Mutex lock failed/n");
- exit(-1);
- }
- threadpool->state = POOL_EXIT;
-
- if (threadpool->thr_alive > 0) {
- if (pthread_cond_broadcast(&(threadpool->pool_cond)) != 0) {
- pthread_mutex_unlock(&threadpool->pool_mutex);
- return;
- }
- }
-
- while (threadpool->thr_alive > 0)
- {
-
- if (pthread_cond_wait (&threadpool->pool_cond, &threadpool->pool_mutex) != 0)
- {
- pthread_mutex_unlock(&threadpool->pool_mutex);
- return;
- }
- }
- if (pthread_mutex_unlock(&threadpool->pool_mutex) != 0) {
- logg("!Mutex unlock failed/n");
- exit(-1);
- }
-
- pthread_mutex_destroy(&(threadpool->pool_mutex));
- pthread_cond_destroy(&(threadpool->pool_cond));
- pthread_attr_destroy(&(threadpool->pool_attr));
- free(threadpool);
- return;
- }
分配线程工作
- int thrmgr_dispatch(threadpool_t *threadpool, void *user_data)
- {
- pthread_t thr_id;
- if (!threadpool)
- {
- return FALSE;
- }
-
-
- if (pthread_mutex_lock(&(threadpool->pool_mutex)) != 0)
- {
- logg("!Mutex lock failed/n");
- return FALSE;
- }
- if (threadpool->state != POOL_VALID)
- {
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0)
- {
- logg("!Mutex unlock failed/n");
- return FALSE;
- }
- return FALSE;
- }
- work_queue_add(threadpool->queue, user_data);
-
- if ((threadpool->thr_idle == 0) && (threadpool->thr_alive < threadpool->thr_max))
- {
-
- if (pthread_create(&thr_id, &(threadpool->pool_attr),thrmgr_worker, threadpool) != 0)
- {
- logg("!pthread_create failed/n");
- }
- else
- {
- threadpool->thr_alive++;
- }
- }
-
- pthread_cond_signal(&(threadpool->pool_cond));
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0)
- {
- logg("!Mutex unlock failed/n");
- return FALSE;
- }
- return TRUE;
- }
分配线程工作,大致两步 1.把工作加入队列当中, 2. 通知线程有工作了,这里用线程条件变量
线程接到信号后就会去工作了:
- void *thrmgr_worker(void *arg)
- {
- threadpool_t *threadpool = (threadpool_t *) arg;
- void *job_data;
- int retval, must_exit = FALSE;
- struct timespec timeout;
-
- for (;;)
- {
- if (pthread_mutex_lock(&(threadpool->pool_mutex)) != 0)
- {
-
- logg("!Fatal: mutex lock failed/n");
- exit(-2);
- }
- timeout.tv_sec = time(NULL) + threadpool->idle_timeout;
- timeout.tv_nsec = 0;
- threadpool->thr_idle++;
- while (((job_data=work_queue_pop(threadpool->queue)) == NULL)&& (threadpool->state != POOL_EXIT))
- {
-
- retval = pthread_cond_timedwait(&(threadpool->pool_cond),&(threadpool->pool_mutex), &timeout);
- if (retval == ETIMEDOUT)
- {
- must_exit = TRUE;
- break;
- }
- }
- threadpool->thr_idle--;
- if (threadpool->state == POOL_EXIT)
- {
- must_exit = TRUE;
- }
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0)
- {
-
- logg("!Fatal: mutex unlock failed/n");
- exit(-2);
- }
- if (job_data)
- {
- threadpool->handler(job_data);
- }
- else if (must_exit)
- {
- break;
- }
- }
- if (pthread_mutex_lock(&(threadpool->pool_mutex)) != 0)
- {
-
- logg("!Fatal: mutex lock failed/n");
- exit(-2);
- }
- threadpool->thr_alive--;
- if (threadpool->thr_alive == 0)
- {
-
- pthread_cond_broadcast(&threadpool->pool_cond);
- }
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0)
- {
-
- logg("!Fatal: mutex unlock failed/n");
- exit(-2);
- }
- return NULL;
- }
这个函数是每个线程工作函数,但其实真正在干事的只有 threadpool->handler(job_data);其它的都是做一些判断工作,比如有没有job要做啊等等. 注意这里的for死循环,就是它把线程一直困在了内存中,不然线程函数早就返回了,系统也就回收了这个线程. 这个死循环并不会太耗系统资源,因为线程大部分时间要么在睡眠,要么在工作.不会有忙循环的情况.