基本的线程池如下图所示:
线程池的原理类似一个生产者-消费者的模式。生产者(一般是主线程/IO线程 )将执行函数指针和要传递给函数的参数打包,然后放到任务队列里面;消费者(线程group) 从任务队列中取出任务,得到要执行的函数指针和函数参数,调用函数指针执行任务。执行完之后继续从任务队列里面获取任务。
线程池最大的优点就是多个任务并行执行,但不保证任务执行的顺序。但是在某些环境下,任务队列中的任务会划分成若干子集,每个子集内部需要严格按照顺序执行;但是不同的子集可以并行执行。
例如将多条日志保存到不同的文件:保存到同一个文件的日志有着严格的先后顺序,需要串行写入;但是不同文件之间的日志可以并行写入。
为了满足上述需求,笔者最近开发了一个特殊的线城市----可串行线程池。大概架构如下图所示:
除了维护任务队列之外,还维护了一个运行队列,保存线程group中正在执行的任务。每次取任务时,先判断改任务是否可以与运行队列中的任务并行执行,如果不可以,则暂时不取出该任务,继续判断下一个,直到成功取出任务。
基本接口如下:
//任务结构体
typedef struct {
void (*function)(void *);//任务执行函数
void *argument;//function 参数
void (*freer)(void *);//argument 惰性释放函数
} task_t;
//创建线程池
threadpool_t *threadpool_create(int thread_count, int queue_size, int flags);
//回调函数类型,用于检查两个任务是否可以并行执行
typedef bool check_func(const threadpool_task_t *t1,const threadpool_task_t *t2);
//设置回调函数
int threadpool_set_concurrence_check(threadpool_t *pool,check_func *checker);
//添加任务 pool:线程池 routine:线程回调函数 arg:要传给routine的参数
//释放或delete arg的函数
//因为要在 check_func 中用到arg ,所以在routine不应该是否arg,而由线程池惰性释放
int threadpool_add(threadpool_t *pool, void (*routine)(void *),
void *arg, void (*free)(void *) );
具体代码参见我的git