一、分析线程池工程文件
pool_test/
main.c (使用线程池做的小案例) ------>需要修改,你想需要线程池干什么事,修改即可
thread_pool.c (线程池中函数接口源码) -------不需要修改,直接拿过去用
thread_pool.h (线程池头文件) --------不需要修改,直接拿过去
线程池接口设计书.doc -->说明线程池中函数接口的用法
----->具体这些函数的使用方法在thread_pool.c
二、分析线程池模型”
1.什么是线程池,
线程池就是多个线程组合起来的一个集合。
2.如何表示线程池:
使用一个结构体来描述 ---------- thread_pool.h
thread_pool.h
#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>
#define MAX_WAITING_TASKS 1000
#define MAX_ACTIVE_THREADS 20
struct task
{
void *(*do_task)(void *arg);//任务函数
void *arg; //传递给任务函数的参数
struct task *next; //指向下一个任务叠构提的指针你
};
typedef struct thread_pool
{
pthread_mutex_t lock; //互斥锁
pthread_cond_t cond; //条件变量
bool shutdown; //当前系统的标志位,true---线程池处于关闭状态,flase----线程池处于开启状态
struct task *task_list; //指向任务队列头节点的指针,每一个任务节点都包含的任务函数,执行完这个函数任务就完成了。
pthread_t *tids; //存储线程ID号的空间
unsigned max_waiting_tasks; //线程池中最大等待任务个数
unsigned waiting_tasks; //当前正在等待任务的个数
unsigned active_threads; //当前线程池中线程的个数
}thread_pool;
//任务函数
bool init_pool(thread_pool *pool, unsigned int threads_number);
bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void *task);
int add_thread(thread_pool *pool, unsigned int additional_threads_number);
int remove_thread(thread_pool *pool, unsigned int removing_threads_number);
bool destroy_pool(thread_pool *pool);
void *routine(void *arg);
#endif
二、线程池中的函数接口:
1,线程池初始化:init_pool( )
原型 | bool init_pool(thread_pool * pool, unsigned int threads_number); |
功能描述 | 创建一个新的线程池,包含threads_number个活跃线程 |
参数 | pool: 线程池指针 threads_number: 初始活跃线程个数(大于等于1) |
返回值 | 成功返回true,失败返回false |
所在头文件 | thread_pool.h |
备注 | 线程池最少线程个数为1 |
2,投送任务:add_task( )
原型 | bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void * arg); |
功能描述 | 往线程池投送任务 |
参数 | pool: 线程池指针 do_task: 投送至线程池的执行例程 arg: 执行例程do_task的参数,若该执行例程不需要参数可设置为NULL |
返回值 | 成功返回true,失败返回false |
所在头文件 | thread_pool.h |
备注 | 任务队列中最大任务个数为MAX_WAITING_TASKS |
3,增加活跃线程:add_thread( )
原型 | int add_thread(thread_pool *pool, unsigned int additional_threads); |
功能描述 | 增加线程池中活跃线程的个数 |
参数 | pool: 需要增加线程的线程池指针 additional_threads: 新增线程个数 |
返回值 | >0: 实际新增线程个数 -1: 失败 |
所在头文件 | thread_pool.h |
备注 | 无 |
4,删除活跃线程:remove_thread( )
原型 | int remove_thread(thread_pool *pool, unsigned int removing_threads,); |
功能描述 | 删除线程池中活跃线程的个数 |
参数 | pool: 需要删除线程的线程池指针 removing_threads: 要删除的线程个数。该参数设置为0时直接返回当前线程池线程总数,对线程池不造成任何其它影响 |
返回值 | >0: 当前线程池剩余线程个数 -1: 失败 |
所在头文件 | thread_pool.h |
备注 | 1,线程池至少存在1条活跃线程 2,如果被删除的线程正在执行任务,则将等待其完成任务之后删除 |
5,销毁线程池:destroy_pool( )
原型 | bool destroy_pool(thread_pool *pool,); |
功能描述 | 阻塞等待所有任务完成,然后立即销毁整个线程池,释放所有资源和内存 |
参数 | pool: 将要销毁的线程池 |
返回值 | 成功返回true,失败返回false |
所在头文件 | thread_pool.h |
备注 | 无 |
thread_pool.c
#include "thread_pool.h"
//线程取消例程函数
void handler(void *arg)
{
//1. 输出一下我自己的TID号,告诉大家,我要结束了!
printf("[%u] is ended.\n",
(unsigned)pthread_self());
//2. 线程要退出,可以,但是要先解锁。
pthread_mutex_unlock((pthread_mutex_t *)arg);
}
void *routine(void *arg)
{
//1. 线程先接住线程池变量的地址
thread_pool *pool = (thread_pool *)arg;
struct task *p;
while(1)
{
//2. 设置线程取消例程函数
pthread_cleanup_push(handler, (void *)&pool->lock);
//3. 上锁。
pthread_mutex_lock(&pool->lock);
//4. 当线程池中等待的任务个数为0,并且线程池为开启状态时
//跳出循环的条件:
//1>. 等待任务个数>0
//2>. 线程池的shutdown为true
//1>和2>同时为假,也会跳出循环。
while(pool->waiting_tasks == 0 && !pool->shutdown)
{
//5. 进入条件变量中等待。
pthread_cond_wait(&pool->cond, &pool->lock);
}
//如果当前的任务个数=0 并且 线程池为关闭状态,线程才会退出。
if(pool->waiting_tasks == 0 && pool->shutdown == true)
{
//解锁
pthread_mutex_unlock(&pool->lock);
//走人
pthread_exit(NULL);
}
//1>. 什么时候,线程会从条件变量中出来?
//第一种情况,当投放任务,随机唤醒一个线程,线程因为"pool->waiting_tasks == 0"为假,就会跳出来。
//第二种情况:因为线程池为关闭状态,导致这个条件"!pool->shutdown"为假而跳出循环。
//第三种情况:又有任务做,线程池又关闭,也会跳出来。
//2>. 什么情况下,线程才会退出。
//只有任务队列中没有任务做,并且线程池为关闭状态,线程才会退出。
//3>. 以下的情况,线程都不会退出。
//1> 有任务, 线程池是关闭状态,那么线程就会做完所有的任务之后,再退出。
//2> 有任务, 线程池是开启状态,那么线程就会做完所有的任务之后,去条件变量中睡眠。
//3. 没有任务,线程池为开启状态 -> 这种情况,线程不会从条件变量中醒来。
//4. 没有任务,线程池为关闭状态 -> 线程退出。
//让p指向头节点的下一个节点。
p = pool->task_list->next;
//让头节点的指针域指向p的下一个节点。
pool->task_list->next = p->next;
//当前等待的任务个数-1
pool->waiting_tasks--;
//解锁。
pthread_mutex_unlock(&pool->lock);
//删除线程取消例程函数。
pthread_cleanup_pop(0);
//设置线程当前不能响应取消。
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
//执行p指向的那个节点函数
(p->do_task)(p->arg);
//设置线程当前能响应。(线程说,我做完这个任务了,要杀要打请随便)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
//释放p这个节点。
free(p);
}
pthread_exit(NULL);
}
bool init_pool(thread_pool *pool, unsigned int threads_number)
{
//1. 初始化互斥锁。
pthread_mutex_init(&pool->lock, NULL);
//2. 初始化条件变量。
pthread_cond_init(&pool->cond, NULL);
//3. 设置当前系统的标志位为开启状态。
pool->shutdown = false;
//4. 为任务队列的头节点申请空间。
pool->task_list = malloc(sizeof(struct task));
//6. 为储存ID号的空间申请内存空间,最多容纳20个线程。
pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);
//7. 错误判断,如果空间申请失败,那么初始化失败。
if(pool->task_list == NULL || pool->tids == NULL)
{
perror("allocate memory error");
return false;
}
//8. 为任务队列的头节点的指针域赋值。
pool->task_list->next = NULL;
//9. 设置线程池中最大等待任务个数为1000个。
pool->max_waiting_tasks = MAX_WAITING_TASKS;
//10. 当前线程池没有任务。
pool->waiting_tasks = 0;
//11. 初始化线程的个数。
pool->active_threads = threads_number;
//12. 根据你的需求,来创建线程。
int i;
for(i=0; i<pool->active_threads; i++)
{
if(pthread_create(&((pool->tids)[i]), NULL,routine, (void *)pool) != 0)
{
perror("create threads error");
return false;
}
}
//13. 初始化成功。
return true;
}
bool add_task(thread_pool *pool,void *(*do_task)(void *arg), void *arg)
{
//1. 新任务必须要申请新节点,新节点必须要申请新的空间。
struct task *new_task = malloc(sizeof(struct task));
if(new_task == NULL)
{
perror("allocate memory error");
return false;
}
//2. 为新节点的数据域赋值。
new_task->do_task = do_task;
new_task->arg = arg;
//3. 为新节点的指针域赋值。
new_task->next = NULL;
//4. 上锁。(在线程池中,只要涉及到任务队列,都要上锁)
pthread_mutex_lock(&pool->lock);
//5. 如果当前等待的任务个数>=1000
if(pool->waiting_tasks >= MAX_WAITING_TASKS)
{
//解锁
pthread_mutex_unlock(&pool->lock);
//输出一句话报错
fprintf(stderr, "too many tasks.\n");
//释放刚才申请的空间。
free(new_task);
//添加任务失败
return false;
}
//6. 寻找最后一个节点
struct task *tmp = pool->task_list;
while(tmp->next != NULL)
tmp = tmp->next;
//从循环出来,tmp->next一定为NULL,说明tmp就是指向最后一个节点。
//让最后一个节点的指针域指向新节点。
tmp->next = new_task;
//7. 当前等待的任务个数+1
pool->waiting_tasks++;
//8. 解锁
pthread_mutex_unlock(&pool->lock);
//9. 随机唤醒条件变量中的一个线程起来任务。
pthread_cond_signal(&pool->cond);
//10. 添加成功。
return true;
}
int add_thread(thread_pool *pool, unsigned additional_threads) //5
{
//如果你想新增0条线程,那么函数直接返回。
if(additional_threads == 0)
return 0;
//总线程个数 = 当前线程池中线程个数 + 新增条数
unsigned total_threads = pool->active_threads + additional_threads;
// 7 = 2 + 5
int i, actual_increment = 0;
for(i = pool->active_threads; // i=2
i < total_threads && i < MAX_ACTIVE_THREADS; //i必须小于7并且小于20,就可以创建。
i++)
{
if(pthread_create(&((pool->tids)[i]),NULL, routine, (void *)pool) != 0)
{
perror("add threads error");
if(actual_increment == 0) //如果创建线程时,发现一条都没有到,那么直接返回-1
return -1;
break;
}
actual_increment++; //真正创建的条数+1
}
//从循环出来时,actual_increment就是真正创建线程的条数。
//当前活跃的线程增加了actual_increment这么条。
pool->active_threads += actual_increment;
//最后返回新增的条数。
return actual_increment;
}
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
//如果想删除0条线程,那么就返回当前线程池中剩余的条数。
if(removing_threads == 0)
return pool->active_threads;
// 剩余线程的条数 = 当前线程池中线程条数 - 你想删除的条数。
int remaining_threads = pool->active_threads - removing_threads;
// 5 = 8 - 3
// 0 = 8 - 8
// -2 = 8 - 10
//如果减完之后,余数是大于0的,那么结果按是你的结果。
//如果减完之后,余数是等于0/小于0的,那么结果就等于1。
remaining_threads = remaining_threads > 0 ? remaining_threads : 1;
//取消你想删除的那么多的条线程。
int i;
for(i=pool->active_threads-1; i>remaining_threads-1; i--)
{
errno = pthread_cancel(pool->tids[i]);
if(errno != 0)
break;
}
//如果一条都没有取消得了,返回-1
if(i == pool->active_threads-1)
return -1;
else
{
//如果至少取消了一条,那么也算成功。
pool->active_threads = i+1;
return i+1;
}
}
bool destroy_pool(thread_pool *pool)
{
//1. 先将系统标志位设置为true,代表线程池要关闭了
pool->shutdown = true;
//2. 广播,通知所有的小孩醒来退出了。
pthread_cond_broadcast(&pool->cond);
//3. 接合所有线程。
int i;
for(i=0; i<pool->active_threads; i++)
{
errno = pthread_join(pool->tids[i], NULL);
if(errno != 0)
{
printf("join tids[%d] error: %s\n",
i, strerror(errno));
}
else
printf("[%u] is joined\n", (unsigned)pool->tids[i]);
}
//4. 将申请过的空间释放掉。
free(pool->task_list);
free(pool->tids);
free(pool);
//5. 销毁成功。
return true;
}
线程池的简单实用main函数
#include "thread_pool.h"
void *mytask(void *arg) //线程的任务
{
int n = *(int*)arg;
//工作任务:余数是多少,就睡多少秒,睡完,任务就算完成
printf("[%u][%s] ==> job will be done in %d sec...\n",
(unsigned)pthread_self(), __FUNCTION__, n);
sleep(n);
printf("[%u][%s] ==> job done!\n",
(unsigned)pthread_self(), __FUNCTION__);
return NULL;
}
void *count_time(void *arg)
{
int i = 0;
while(1)
{
sleep(1);
printf("sec: %d\n", ++i);
}
}
int main(void)
{
// 本线程用来显示当前流逝的秒数
// 跟程序逻辑无关
pthread_t a;
pthread_create(&a, NULL, count_time, NULL);
// 1, initialize the pool
thread_pool *pool = malloc(sizeof(thread_pool));
init_pool(pool, 2);
//2个线程都在条件变量中睡眠
// 2, throw tasks
printf("throwing 3 tasks...\n");
int aa = (rand()%10);
int b = (rand()%10);
int c = (rand()%10);
add_task(pool, mytask, (void *)&aa); //5
add_task(pool, mytask, (void *)&b); //6
add_task(pool, mytask, (void *)&c); //3
// 3, check active threads number
printf("current thread number: %d\n",
remove_thread(pool, 0));//2
sleep(9);
// 4, throw tasks
printf("throwing another 6 tasks...\n");
int d,e,f,g,h,j;
d = (rand()%10);
e = (rand()%10);
f = (rand()%10);
g = (rand()%10);
h = (rand()%10);
j = (rand()%10);
add_task(pool, mytask, (void *)&d);
add_task(pool, mytask, (void *)&e);
add_task(pool, mytask, (void *)&f);
add_task(pool, mytask, (void *)&g);
add_task(pool, mytask, (void *)&h);
add_task(pool, mytask, (void *)&j);
// 5, add threads
add_thread(pool, 2);
sleep(5);
// 6, remove threads
printf("remove 3 threads from the pool, "
"current thread number: %d\n",
remove_thread(pool, 3));
// 7, destroy the pool
destroy_pool(pool);
return 0;
}
通过线程池实现过线程文件复制
#include "head.h"
bool copydir(struct tpool *pool,char *src,char *dst,struct stat *info)
{
if(access(dst,F_OK))
{
mkdir(dst,info->st_mode);
}
struct stat *fileinfo = (struct stat *)malloc(sizeof(struct stat));
stat(dst,fileinfo);
if(!S_ISDIR(fileinfo->st_mode))
{
printf("%s Not a directory!\n",dst);
return false;
}
char src_path[50],dst_path[50],ori_path[50];
bzero(ori_path,50);
getcwd(ori_path,50);
chdir(src);
bzero(src_path,50);
getcwd(src_path,50);
chdir(ori_path);
chdir(dst);
bzero(dst_path,50);
getcwd(dst_path,50);
DIR *dp = opendir(src_path);
chdir(src_path);
struct dirent *ep = NULL;
while(1)
{
ep = readdir(dp);
if(ep == NULL)
break;
if(strcmp(ep->d_name,".") == 0 ||
strcmp(ep->d_name,"..") == 0)
continue;
bzero(fileinfo,sizeof(struct stat));
chdir(src_path);
stat(ep->d_name,fileinfo);
if(S_ISREG(fileinfo->st_mode))
{
int *fd = calloc(2, sizeof(int));
fd[0] = open(ep->d_name, O_RDONLY);
chdir(dst_path);
fd[1] = open(ep->d_name, O_WRONLY|O_CREAT|O_TRUNC,fileinfo->st_mode);
add_task(pool,mytask,(void *)fd);
}
if(S_ISDIR(fileinfo->st_mode))
{
chdir(dst_path);
mkdir(ep->d_name,fileinfo->st_mode);
chdir(ep->d_name);
char new_path[50];
getcwd(new_path,50);
chdir(src_path);
copydir(pool,ep->d_name,new_path,fileinfo);
}
}
}
void copyfile(int src,int dst)
{
char buf[1024];
int r_num;
while(1)
{
r_num = read(src,buf,1024);
if(r_num <= 0)
break;
write(dst,buf,r_num);
}
return;
}
void *mytask(void *arg)
{
int *fd = (int *)arg;
copyfile(fd[0],fd[1]);
}
int main(int argc,char *argv[]) // ./copy file1 file2 || ./copy dir1 dir2 || ./copy file dir/
{
umask(0000);
if(argc != 3)
{
printf("argc error!\n");
exit(0);
}
struct tpool *pool = malloc(sizeof(struct tpool));
init_pool(pool,20);
struct stat *fileinfo = malloc(sizeof(struct stat));
stat(argv[1],fileinfo);
struct stat *dstfileinfo = malloc(sizeof(struct stat));
stat(argv[2],dstfileinfo);
if(S_ISREG(fileinfo->st_mode) && !S_ISDIR(dstfileinfo->st_mode))
{
int *fd = calloc(2, sizeof(int));
fd[0] = open(argv[1],O_RDONLY);
fd[1] = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,fileinfo->st_mode);
if(fd[0] == -1 || fd[1] == -1)
{
exit(0);
}
add_task(pool,mytask,(void *)fd);
}
if(S_ISREG(fileinfo->st_mode) && S_ISDIR(dstfileinfo->st_mode))
{
int *fd = calloc(2, sizeof(int));
fd[0] = open(argv[1],O_RDONLY);
chdir(argv[2]);
fd[1] = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,fileinfo->st_mode);
if(fd[0] == -1 || fd[1] == -1)
{
exit(0);
}
add_task(pool,mytask,(void *)fd);
}
if(S_ISDIR(fileinfo->st_mode))
{
copydir(pool,argv[1],argv[2],fileinfo);
}
destroy_pool(pool);
return 0;
}
用