本文就来实现一个线程池。实现环境为Linux操作系统,采用C语言实现pthread线程的线程池。
线程分配设计:
线程池中的每个子线程都是等价的。我们用线程信号量来控制子线程和任务的分配问题。设置一个信号量来表示任务队列中的任务资源。每个子线程都会处于死循环中,每轮循环首先等待一个任务资源信号量,当等到之后,互斥地从任务队列中摘取一个任务结点,任务结点中记录着该任务所要执行的函数指针及其参数。之后子线程开始执行该任务。执行完之后释放一个信号量并进入下一轮循环。当没有信号量小于1时,子线程将会阻塞。
因此一个任务由哪一个线程来执行,这要看哪个线程能够获取到对应的信号量资源。
具体实现:
任务队列由双向链表构造,每个节点包含一个任务的函数指针和参数指针。
一般一个简单的线程池有下列组件:
//threadpool.h
/**********************************************
* @author Jacky Lau
* @date 10/26/2014
*
*
* **********************************************/
#include
#include
/*========================STRUCTS==========================*/
typedef void *(*function_t)(void *arg);
/* jobs */
typedef struct thread_job_t{
function_t pf; /**< function pointer */
void *arg; /**< function's pointer */
struct thread_job_t *next; /**< pointer to next job */
struct thread_job_t*prev; /**< pointer to previous job */
}thread_job_t;
/* threadpool */
typedef struct threadpool_t{
pthread_t *threads; /**< pointer to thread's ID */
int num_threads; /**< number of threads */
thread_job_t *head; /**< pointer to head of workqueue */
thread_job_t *tail; /**< pointer to tail of workqueue */
int num_jobs; /**< number of jobs in the workqueue*/
sem_t *queue_sem;
}threadpool_t;
/*==========================FUNCTION=================================*/
/*-------------------------threadpool specific------------------------------- */
/**
* @brief initialize threadpool
* Allocates memory for the threadpool, workqueue,semphore and fixes pointer
* in job queue
* @param num_threads to be used
* @return pointer to threadpool_t struct on success,
* NULL on error
*/
threadpool_t *threadpool_init(int num_threads);
/**
* @brief destroy threadpool
* if there are so many jobs and can't handle all of them, free the job_queue
* if there are so many threads, wake all of them in case of zoombie
* @param threadpool
*/
void threadpool_destroy(threadpool_t *threadpool);
/*-------------------------work specific--------------------------------- */
/**
* @brief do the jobs, all of the threads are blocked when they first created
* @param threadpool_in
*/
void threadpool_thread_do(threadpool_t *threadpool_in);
/**
* @brief add the job into the job_queue, it will wake the blocked threads by sem_post
* @param threadpool
* @param pointer to the job function
* @param argument's of function
* @return 0 on success,other on failure
*/
int threadpool_add_work(threadpool_t *threadpool, function_t pf, void *arg);
/*-------------------------queue specific--------------------------------- */
/**
* @brief add job into the queue
* @param threadpool
* @param pointer to the struct of thread_job_t
* @return 0 on success,
* other on failure
*/
int threadpool_job_enqueue(threadpool_t *threadpool, thread_job_t *job);
/**
* @brief remove the first data unit of queue
* @param threadpool
* @return 0 on success
* other on failure
*/
thread_job_t *threadpool_job_dequeue(threadpool_t *threadpool);
/**
* @brief make the job_queue empty
* @param threadpool
* @return 0 on success
* other on failure
*/
int threadpool_job_queue_empty(threadpool_t *threadpool);
#include"threadpool.h"
#include
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef int bool;
#define true 1
#define false 0
bool thread_islive = true;
#define CHECK_ERROR(a) \
if((a)) \
{ \
perror("Error at line\n\t" #a "\nSystem Msg");\
exit(EXIT_FAILURE); \
}
/*==================FUNCTIONS========================*/
threadpool_t *threadpool_init(int num_threads)
{
if(num_threads < 1)
num_threads =1;
/* malloc the struct threadpool*/
threadpool_t *threadpool = (threadpool_t *) malloc(sizeof(threadpool_t));
/*malloc the threads' ID */
threadpool->threads = (pthread_t *)malloc(sizeof(pthread_t) * num_threads);
threadpool->num_threads = num_threads;
/*initialize the workqueue*/
threadpool->head = NULL;
threadpool->tail = NULL;
threadpool->num_jobs = 0;
/*initialize the semaphore*/
threadpool->queue_sem = (sem_t *)malloc(sizeof(sem_t));
sem_init(threadpool->queue_sem, 0, 0);
/*make the threads in pool*/
int i;
for(i = 0; i < num_threads; i++)
{
pthread_create(&threadpool->threads[i], NULL, (void *)threadpool_thread_do, (void *)threadpool);
}
return threadpool;
}
/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
void threadpool_destroy(threadpool_t *threadpool)
{
thread_islive = false;
int i;
/*because create threads more than jobs, the threads will be zoombie*, so wake all of them*/
for(i = 0; i < threadpool->num_threads; i++)
{
CHECK_ERROR(sem_post(threadpool->queue_sem));
}
CHECK_ERROR(sem_destroy(threadpool->queue_sem));
/*barrier for synchronization*/
for(i = 0; i < threadpool->num_threads; i++)
pthread_join(threadpool->threads[i], NULL);
/*dealloc job queue*/
/*if the jobs is so many and can't handle them, it will cause mem leak, so empty the queue*/
threadpool_job_queue_empty(threadpool);
/*dealloc threads*/
free(threadpool->threads);
/*dealloc semphore*/
free(threadpool->queue_sem);
/*dealloc threadpool*/
free(threadpool);
}
int threadpool_job_queue_empty(threadpool_t *threadpool)
{
thread_job_t *prev = threadpool->head;
thread_job_t *p = prev;
while(threadpool->num_jobs)
{
p = p->next;
free(prev);
prev = p;
threadpool->num_jobs--;
}
threadpool->head = NULL;
threadpool->tail = NULL;
return 0;
}
void threadpool_thread_do(threadpool_t *threadpool_in)
{
assert(threadpool_in);
threadpool_t *threadpool = threadpool_in;
if(sem_wait(threadpool->queue_sem))
{
perror("thread waiting for semaphore");
exit(EXIT_FAILURE);
}
while(thread_islive)
{
pthread_mutex_lock(&mutex); /* LOCK */
thread_job_t *job= threadpool_job_dequeue(threadpool);
pthread_mutex_unlock(&mutex); /* UNLOCK */
if(job)
{
function_t pf = job->pf;
void *arg = job->arg;
pf(arg);
free(job);
}
}
}
thread_job_t *threadpool_job_dequeue(threadpool_t *threadpool)
{
assert(threadpool);
thread_job_t *job = threadpool->head;
switch (threadpool->num_jobs) {
case 0:
job = NULL;
break;
case 1:
threadpool->head = NULL;
threadpool->tail = NULL;
threadpool->num_jobs--;
break;
default:
threadpool->head = job->next;
job->next = NULL;
threadpool->head->prev = NULL;
threadpool->num_jobs--;
break;
}
return job;
}
int threadpool_add_work(threadpool_t *threadpool, function_t pf, void *arg)
{
assert(threadpool);
thread_job_t *job = (thread_job_t *)malloc(sizeof(thread_job_t));
job->pf = pf;
job->arg = arg;
job->next = NULL;
job->prev = NULL;
pthread_mutex_lock(&mutex); /* LOCK */
CHECK_ERROR( threadpool_job_enqueue(threadpool, job) );
pthread_mutex_unlock(&mutex); /* UNLOCK */
return 0;
}
int threadpool_job_enqueue(threadpool_t *threadpool, thread_job_t *job)
{
assert(threadpool);
assert(job);
switch(threadpool->num_jobs)
{
case 0:
threadpool->head = job;
threadpool->tail = job;
threadpool->num_jobs++;
break;
default:
threadpool->tail->next = job;
job->prev = threadpool->tail;
threadpool->tail = job;
threadpool->num_jobs++;
}
sem_post(threadpool->queue_sem);
return 0;
}
/********************************************
*@author JackyLau
*@date 10/26/2014
*
*
* ******************************************/
#include
#include
#include"threadpool.h"
void task1()
{
printf("thread %d running Task 1\n", (int)pthread_self());
int i;
/*for delay*/
int sum = 0;
for(i = 0; i < 100000; i++)
{
sum += i;
}
}
void task2(int id)
{
printf("%d thread %d running Task 2\n", id, (int)pthread_self());
/*for delay*/
int i;
int sum = 0;
for(i = 0; i < 100000; i++)
{
sum += i;
}
}
int main(void)
{
threadpool_t *threadpool = threadpool_init(10);
int i;
for(i = 0; i < 1000; i++)
{
threadpool_add_work(threadpool, (void *)task1, NULL);
threadpool_add_work(threadpool, (void *)task2, (void *)i);
}
sleep(5);/*so they can handle all of the jobs, or just a bit of them*/
threadpool_destroy(threadpool);
//printf("Hello World!\n");
return 0;
}
运行结果: