/threadpool

一个Linux下基于pthread线程池的实现

Primary LanguageC

Linux下的C线程池

这是一个linux系统下使用C语言编写的基于pthread的线程池,通过预先创建线程,节省线程创建和销毁的时间来提高性能。由于工作线程和管理线程之间使用eventfd机制通信,所以只能在Linux系统下运行,并且eventfd占用了不少文件描述符。

创建线程池

使用thread_pool_create()创建线程池,可以指定线程池内的线程数量。目前这个数量只能在创建时声明,线程池不会动态增加线程数量。传入THREAD_NUM_CPU则会创建与CPU核心数量一样的线程数。

struct thread_pool * thread_pool_create(int thread_num);

创建任务并放入线程池队列

有两种方法可以创建任务并将任务放入线程池队列,第一种是显式的调用thread_task_create()创建,然后调用thread_pool_queue_task()将其放入线程池中:

thread_task_t * thread_task_create(task_func func, void * arg);
int thread_pool_queue_task(struct thread_pool * pool, thread_task_t * task);

第二种是隐式的调用thread_pool_new_task()创建任务,线程池会负责分配thread_task_t结构并将其放入队里中:

int thread_pool_new_task(struct thread_pool * pool, task_func func, void * arg);

两种方法的区别在于第一种方法调用者可以获得指向thread_task_t结构的指针,因此可以对任务做些额外的事情,例如等待任务完成后获取任务函数的返回值,但调用者必须自己负责清理thread_task_t结构,方法是调用thread_task_destroy()函数。第二种方法则不需要调用者做额外的事情,线程池会负责申请并清理thread_task_t结构,但调用者就拿不到任务函数的返回值了,因此第二种方法更适合执行不需要返回值的任务。

目前线程池的队列调度算法是简单的LIFO(栈结构)。

等待任务完成

对于使用thread_task_create()创建的任务,可以先调用thread_task_wait()等待任务完成后,再调用thread_task_get_result()获得任务函数的返回值:

int thread_task_wait(thread_task_t * task);
void * thread_task_get_result(thread_task_t * task);

另外,对于线程池,可以调用thread_pool_wait_all等待放入线程池任务队列中所有的任务完成:

void thread_pool_wait_all(struct thread_pool * pool);

销毁线程池

通过thread_pool_destory()可以销毁一个线程池,这个函数会阻塞直到所有工作线程和管理线程退出。非常不建议在程序中途使用这个函数,可能会造成资源泄漏。如果一定要使用这个函数,请先调用thread_pool_wait_all()等待所有队列中的任务执行完毕,这样造成资源泄漏的概率要小的多。

void thread_pool_destory(struct thread_pool * pool);

其他说明

这个线程池实现了最基础的功能,我个人认为比较适合于计算密集型的应用,另外,线程池是基于pthread的,所以放入线程池中的任务函数不能调用某些pthread函数,例如pthread_exit(),调用了这个函数会导致工作线程退出,接下来会发生什么事情我也不知道了。当然,像pthread_mutex_t这类锁的调用,只要实现正常,应该是可以用的。