问题描述:
在我们的日常生活中,如果我们现在要浏览一个网页或者频繁的打开一个执行时间较短的任务,
如果每次调用都创建一个线程,使用结束后就立即释放,那么这样的开销对于操作系统来说有点太过浪费了。
而线程池的出现就可以很好地解决这样的问题。
线程池:
1.特点:
1>经典生产者消费者模型。
2>线程池中有若干等待的线程。
3>线程池中的线程用于执行大量的相对短暂的任务。
2.功能描述:
当任务增加时,可以动态增加线程池中线程的个数,
当任务执行完成后,可以动态的减少线程池中线程的个数。
生产者线程向任务队列中添加任务,任务队列中有任务,如果有等待线程就唤醒并执行任务
如果线程池中没有等待线程并且没有达到上限,就添加新的线程到线程池
3.线程池的创建场景:
1>计算密集型任务: 线程池线程个数 = CPU个数
2>I/O密集型任务:线程池线程个数 > CPU个数
线程池的创建思路:
线程池的结构体框架:
//任务队列整体的结构体 typedef struct threadpool{ condition_t cond; //同步、互斥 task_t* first; //任务队列的队头 task_t* tail; //任务队列的队尾 int max_thread; //最大线程数 int idle; //空闲线程个数 int counter; //线程池当前的线程个数 }threadpool_t;
任务队列结点的结构体:
typedef struct task{ void* (*pfun)(void*); //任务队列的回调函数 void* arg; //回调函数的参数 struct task* _next; }task_t;
互斥量的结构体:
//互斥操作的结构体 typedef struct condition{ pthread_mutex_t pmutex; pthread_cond_t pcond; }condition_t;
数据组织的数据结构:先来的任务先执行,后来的任务后执行。所以选择队列。
对线程池的操作:
1.
线程池初始化:需要给出要初始化线程池对象,线程池线程的最大数量
void threadpool_init(threadpool_t* pool, int max);
添加任务:需要给出线程池,任务函数,及任务函数的参数
void threadpool_add(threadpool_t* pool, void*(*pf)(void*), void* arg);
销毁线程池:需要给出线程池(动态的销毁线程池---当一定时间内,某线程一直没有被调用过,认为可以将该线程销毁)
void threadpool_destroy(threadpool_t* pool);
线程池完整代码:
1.main.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include "threadpool.h" //主函数功能描述: //创建一个线程池,初始化最大线程3, 添加10个任务 void* run(void* arg) { int id = *(int*)arg; free(arg); printf("%#lX thread running ! id = %d\n", pthread_self(), id); sleep(1); } int main() { threadpool_t pool; threadpool_init(&pool, 3); int i = 0; for(; i < 10; i++) { int *p = (int*)malloc(sizeof(int)); *p = i; threadpool_add(&pool, run, (void*)p); } threadpool_destroy(&pool); sleep(15); }
2.threadpool.h
#ifndef __THREADPOOL_H__ #define __THREADPOOL_H__ #include "condition.h" typedef struct task{ //队列结点的结构体 void* (*pfun)(void*); //任务队列的回调函数 void* arg; //回调函数的参数 struct task* _next; //链表指针 }task_t; typedef struct threadpool{ condition_t cond; //同步、互斥操作 task_t* first; //任务队列的队头 task_t* tail; //任务队列的队尾 int max_thread; //最大线程数 int idle; //空闲线程的个数 int counter; //线程池当前的线程个数 int quit; //为1表示退出,为0表示不退出 }threadpool_t; //初始化 void threadpool_init(threadpool_t* pool, int max); //往线程池中添加任务 void threadpool_add(threadpool_t* pool, void*(*pf)(void*), void* arg); //销毁线程池(动态的销毁线程池---当一定时间内,某线程一直没有被调用过,认为可以将该线程销毁) void threadpool_destroy(threadpool_t* pool); #endif// __THREADPOOL_H__
3.threadpool.c
#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include "threadpool.h" //线程池功能描述: //当任务增加时,可以动态增加线程池中线程的个数, //当任务执行完成后,可以动态的减少线程池中线程的个数。 //生产者线程向任务队列中添加任务,任务队列中有任务,如果有等待线程就唤醒并执行任务, //如果线程池中没有等待线程并且没有达到上限,就添加新的线程到线程池 //线程执行的任务函数 void* thread_fun(void* arg) { threadpool_t* pool = (threadpool_t*)arg; int timeout = 0; while(1) //线程没有任务也不退出 { condition_lock(&pool->cond); pool->idle++; //线程没有执行任务,先置为空闲线程 timeout = 0; while(pool->first == NULL && pool->quit == 0) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); //获取当前的绝对时间。 ts.tv_sec += 2; int r = condition_timedwait(&pool->cond, &ts); if(r == ETIMEDOUT) { timeout = 1; break; } } pool->idle--; if(NULL != pool->first) { task_t *p_first = pool->first; //取出队头结点 pool->first = p_first->_next; condition_unlock(&pool->cond); //防止调用的任务执行时间过长,导致别的线程无法进入22行的lock,甚至于往线程池添加任务的锁都无法进入 //这里的解锁不会影响对链表的操作,因为这里将链表中的单个结点拿出来进行操作,并没有涉及到对链表的操作(没有操作线程之间共享的数据) (p_first->pfun)(p_first->arg); //调用pool传递进来的任务函数 condition_lock(&pool->cond); free(p_first); p_first = NULL; } if(NULL == pool->first && 1 == timeout) //防止其他线程添加任务,这时应该去执行相应的任务, { //此时确定是线程2秒内没有任务,导致的退出 condition_unlock(&pool->cond); printf("%#lX thread TIMEOUT! QUIT!!!\n", pthread_self()); break; } if(pool->quit == 1 && NULL == pool->first) { printf("%#lX thread destroy!\n", pthread_self()); pool->counter--; if(0 == pool->counter) { condition_signal(&pool->cond); //这里发送信号提示最后一个线程任务结束 } condition_unlock(&pool->cond); break; } condition_unlock(&pool->cond); } } void threadpool_init(threadpool_t* pool, int max) { assert(NULL != pool); condition_init(&pool->cond); pool->first = NULL; pool->tail = NULL; pool->max_thread = max; pool->idle = 0; pool->counter = 0; pool->quit = 0; } //往线程池添加线程 void threadpool_add(threadpool_t* pool, void*(*pf)(void*), void* arg) { //先将任务生成任务节点 task_t* new_task = (task_t*)malloc(sizeof(task_t)); new_task->pfun = pf; new_task->arg = arg; new_task->_next = NULL; //由于队列的插入操作、及线程池线程的调用操作是不能被打断的 //所以以下动作需加锁 condition_lock(&pool->cond); //将任务插入任务队列等待调度 if(NULL == pool->first) { pool->first = new_task; } else { pool->tail->_next = new_task; } pool->tail = new_task; //跳整队尾指针 //当线程池中存在空闲线程时,直接唤醒空闲线程 if(pool->idle > 0) //有空闲线程 { condition_signal(&pool->cond); } //当线程池中没有空闲线程且线程总数没有超出最大线程,则创建线程并执行 else if(pool->counter < pool->max_thread) { pthread_t tid; pthread_create(&tid, NULL, thread_fun, (void*)pool); pool->counter++; } condition_unlock(&pool->cond); } void threadpool_destroy(threadpool_t* pool) { if(pool->quit) return; condition_lock(&pool->cond); pool->quit = 1; //将所有的线程状态置为退出态 if(pool->counter > 0) { if(pool->idle > 0) condition_broadcast(&pool->cond); } while(pool->counter > 0) { condition_wait(&pool->cond); } condition_unlock(&pool->cond); condition_destroy(&pool->cond); }
4.condition.h
#ifndef __CONDITION_H__ #define __CONDITION_H__ #include <pthread.h> typedef struct condition{ pthread_mutex_t pmutex; pthread_cond_t pcond; }condition_t; int condition_init(condition_t* cond); int condition_lock(condition_t* cond); int condition_unlock(condition_t* cond); int condition_wait(condition_t* cond); int condition_timedwait(condition_t* cond, const struct timespec* abstime); //超时等待 int condition_signal(condition_t* cond); //唤醒一个线程 int condition_broadcast(condition_t* cond); //唤醒所有的线程 int condition_destroy(condition_t* cond); #endif// __CONDITION_H__5.condition.c
#include <pthread.h> #include "condition.h" int condition_init(condition_t* cond) { return pthread_cond_init(&cond->pcond, NULL) && pthread_mutex_init(&cond->pmutex, NULL); } int condition_lock(condition_t* cond) { return pthread_mutex_lock(&cond->pmutex); } int condition_unlock(condition_t* cond) { return pthread_mutex_unlock(&cond->pmutex); } int condition_wait(condition_t* cond) { return pthread_cond_wait(&cond->pcond, &cond->pmutex); } int condition_timedwait(condition_t* cond, const struct timespec* abstime) { return pthread_cond_timedwait(&cond->pcond, &cond->pmutex, abstime); } int condition_signal(condition_t* cond) { return pthread_cond_signal(&cond->pcond); } int condition_broadcast(condition_t* cond) { return pthread_cond_broadcast(&cond->pcond); } int condition_destroy(condition_t* cond) { return pthread_mutex_destroy(&cond->pmutex) && pthread_cond_destroy(&cond->pcond); }
6.Makefile
.PHONY: all clean all: test test: condition.o main.o threadpool.o gcc $^ -o $@ -lpthread -lrt %.o:%.c gcc -c $^ -o $@ clean: rm -rf *.o test