- 线程池的目的是为了减少线程创建、销毁所带来的代价,当有非常多的任务需要独立的线程去做时,可以使用线程池,从线程池中获取线程来处理一个个任务。
本文就来实现一个线程池。实现环境为Linux操作系统,采用C语言实现pthread线程的线程池。
线程分配设计:
线程池中的每个子线程都是等价的。我们用线程信号量来控制子线程和任务的分配问题。设置一个信号量来表示任务队列中的任务资源。每个子线程都会处于死循环中,每轮循环首先等待一个任务资源信号量,当等到之后,互斥地从任务队列中摘取一个任务结点,任务结点中记录着该任务所要执行的函数指针及其参数。之后子线程开始执行该任务。执行完之后释放一个信号量并进入下一轮循环。当没有信号量小于1时,子线程将会阻塞。
因此一个任务由哪一个线程来执行,这要看哪个线程能够获取到对应的信号量资源。
具体实现:
任务队列由双向链表构造,每个节点包含一个任务的函数指针和参数指针。
一般一个简单的线程池有下列组件:
- 线程池管理器(ThreadPoolManager):用于创建并管理线程池
- 工作线程(WorkThread): 线程池中线程
- 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
- 任务队列:用于存放没有处理的任务。提供一种缓冲机制。
//threadpool.h
/********************************************** * @author Jacky Lau * @date 10/26/2014 * * * **********************************************/ #include<pthread.h> #include<semaphore.h> /*========================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);
//threadpool.c#include"threadpool.h" #include<stdio.h> #include<malloc.h> #include<stdlib.h> #include<assert.h> 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; }
//main.c
运行结果:/******************************************** *@author JackyLau *@date 10/26/2014 * * * ******************************************/ #include <stdio.h> #include <unistd.h> #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; }
线程池原理并用C语言实现
最新推荐文章于 2024-09-10 16:02:43 发布