(1)思想
1.一个进程中的线程就好比是一家公司里的员工,员工的数目应该根据公司的业务多少来
定,太少了忙不过来,但是太多了也浪费资源。
2.最理想的情况是:让进程有一些初始数目的线程(所谓的线程池),当没有任务的时候这些线程自动进入睡眠,
有了任务他们会立即执行任务,不断循环。
3.进程还应该可以根据自身任务的繁重与否来增删线程的数目。
4.当所有的任务都完成了之后,所有的线程还能妥当地收官走人,不带走一片云彩。
5.在实际应用过程中, 将线程池编译成库。
(2)达到的目的
1.用户通过init_pool(&pool, n)来初始化一个线程池, 初始线程数为5n
2.用户通过add_task(&pool,do_task, "arg")来向任务链表中投递任务
3.当n个线程能及时处理任务链表里面的任务时(任务链表没有任务排队), 空闲的线程休眠, 新任务添加时唤醒
4.当n个线程不能及时处理任务链表里面的任务时(任务链表有任务排队), 创建新线程来执行任务
5.能创建的线程数上线为MAX个
6.当任务不再繁忙时, 要取消额外创建的线程
(3)框架构想
主线程: 投递任务
↓(投递任务)
任务链表: 任务1-->任务2->任务3...................
↓
↓(取出第一个任务)
|-------------------------|
|线程1 线程2 线程3 ... |
|-------------------------|
1.任务链表被多个线程操作, 需要提供互斥锁来保护"共享资源"
2.限制创建线程的最大个数
3.向外提供初始化线程池的接口
1.根据用户的要求创建初始线程个数为n的线程池
2.初始化一个任务链表
4.内部线程池的线程处理函数
上锁 // 如果这里不上锁,有可能在判断过程中添加了新任务
注册线程清理函数(防止线程被取消时,没有释放互斥锁)
1.任务链表为空:
当前线程个数 >= 用户初始创建的线程个数?
否: 当前线程睡眠在条件变量上(while + wait)
是: 关闭当前线程 (现在任务不繁忙, 空闲线程占用系统资源)
2.任务链表不为空:
1.当前线程从链表中取出第一个任务
2.从任务链表中删除当前任务
解锁 // 先解锁后执行的原因是: 任务执行期间不应该阻止其他线程获取任务
3.执行任务
4.当前任务执行完毕后, 重复上面的操作
5.向外提供主线程投递任务到任务链表的接口
上锁
1.将任务投递到链表尾部
2.当前有正在睡眠的线程?
有: 广播所有在条件变量上睡眠的线程
无: 创建线程 // 当前任务繁忙, 用户要求创建的线程不够用
解锁
用新建线程执行该任务
6.向外提供销毁线程池的接口
(4) 源代码及测试程序
my_pthread_pool.h
#ifndef MY_PTHREAD_POOL_H
#define MY_PTHREAD_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 CUR printf("%s: %d\n", __FILE__, __LINE__)
#define pr printf
//#define CUR
//#define PR
#define PTHREAD_MAX 20
typedef void *(*task)(void *arg);
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
// 任务链表
typedef struct task {
task do_task; // 定义了一个void *(*)(void *)的函数指针
void *arg;
struct task *next;
}task_t;
// tid链表
typedef struct tid_info {
pthread_t tid; // 定义了一个void *(*)(void *)的函数指针
pthread_attr_t attr;
struct tid_info *next;
}tid_info_t;
typedef struct thread_pool {
task_t *task_list;
tid_info_t *tid_info_list;
u32 n_cur_task; // 链表中任务个数
u32 n_cur_thread; // 当前存在的线程个数
u32 n_usr_thread; // 用户要求创建的线程个数
u32 n_wait_thread; // 当前正在睡眠的线程个数
pthread_attr_t attr;
pthread_cond_t cond;
pthread_mutex_t mutex;
}thread_pool_t;
int destory_pool(thread_pool_t *pool);
int add_task(thread_pool_t *pool, task do_task, void *arg);
int init_pool(thread_pool_t *pool, u32 pthread_num);
#endif //MY_PTHREAD_POOL_H
my_pthread_pool.c
#include "my_pthread_pool.h"
void add_tid_info(thread_pool_t *pool, tid_info_t *new_info)
{
tid_info_t *tmp = pool->tid_info_list;
if (!tmp) {
pool->tid_info_list = new_info;
pool->tid_info_list->next = NULL;
}
else {
while(tmp && tmp->next)
tmp = tmp->next;
tmp->next = new_info; // tmp是链表的最后一项
tmp->next->next = NULL;
}
}
// 线程的清理函数
static void clean(void *arg)
{
thread_pool_t *pool = (thread_pool_t *)arg;
pthread_mutex_unlock(&pool->mutex);
}
static void cancel_tid_pthread(thread_pool_t *pool, pthread_t tid)
{
tid_info_t *tmp = pool->tid_info_list; // 链表头
tid_info_t *pre; // 链表头
// 关闭线程
pthread_cancel(tid);
pool->n_cur_thread--;
printf("cancel: n_cur_thread = %d\n", pool->n_cur_thread);
if (tid == tmp->tid){ // 该线程tid信息是链表中的第一项
pool->tid_info_list = tmp->next; // 链表头改为第二项
goto free;
}
// 在链表中找到tid_info结构, 删除
while(tmp) {
if(tid == tmp->tid) // tmp项是要删除的结构
break;
pre = tmp;
tmp = tmp->next;
}
pre->next = tmp->next; // tmp的上一项指向tmp的下一项
// 释放tid_info结构
free:
free(tmp);
}
// 线程处理函数, 传入线程池
static void *work(void *arg)
{
thread_pool_t *pool = (thread_pool_t *)arg;
task_t *head;
while(1) {
// 1.注册线程清理函数
pthread_cleanup_push(clean, (void *)pool);
pthread_mutex_lock(&pool->mutex);
// 2.当前没有任务要处理
if (0 == pool->n_cur_task){
// 当前线程个数 > 用户初始创建的线程个数
if (pool->n_cur_thread > pool->n_usr_thread)
cancel_tid_pthread(pool, pthread_self()); // 关闭当前线程。任务不繁忙, 不需要这么多线程
// 当前线程个数 <= 用户初始创建的线程个数
else { // 当前线程睡眠条件变量cond上
pool->n_wait_thread++; // 当前正在睡眠的线程个数
pr("n_wait_thread = %d\n", pool->n_wait_thread);
pthread_cond_wait(&pool->cond, &pool->mutex);
while(0 == pool->n_cur_task) // 存在假唤醒情况, 所以加了while(0 == pool->n_cur_task)
pthread_cond_wait(&pool->cond, &pool->mutex);
pool->n_wait_thread --; // 当前正在睡眠的线程个数
pr("n_wait_thread = %d\n", pool->n_wait_thread);
}
}
// 3.当前有任务要处理或者被唤醒
head = pool->task_list; // 取出任务链表中的第一个任务
pool->task_list = head->next; // 从链表中删除任务
pool->n_cur_task--; // 链表中的任务个数
pr("task_list: = %d\n", pool->n_cur_task);
pr("npthread: = %d\n", pool->n_cur_thread);
pthread_mutex_unlock(&pool->mutex);
pthread_cleanup_pop(0); // 执行到这里,锁已经被解开了, 不用执行清理函数
// 执行任务
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
head->do_task(head->arg);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
// 取消禁止
free(head); // head已经从链表中删除了, 不是共享数据
//sleep(1);
}
pthread_exit(NULL);
}
// 创建分离线程, 传入创建的个数
static u32 create_detach_pthread(thread_pool_t *pool, int num)
{
int i;
tid_info_t *new_info;
for (i = 0; i < num; i++) {
new_info = malloc(sizeof(tid_info_t));
if (!new_info) {
perror("malloc err");
return -1;
}
pthread_attr_init(&new_info->attr);
pthread_attr_setdetachstate(&new_info->attr,PTHREAD_CREATE_DETACHED); // 设置线程的分离属性
// 创建线程来处理任务
if(pthread_create(&new_info->tid, &new_info->attr, work, (void *)pool)) {
perror("ERR: creat pthread");
return -1;
}
// 将tid_info挂入链表
add_tid_info(pool, new_info);
pool->n_cur_thread++; // 当前存在的线程个数
}
return 0;
}
/* 传入线程池指针, 要创建的线程个数,成功返回0,失败返回-1 */
int init_pool(thread_pool_t *pool, u32 pthread_num)
{
int i, ret;
tid_info_t *new_info;
// 1.初始化条件变量和互斥锁
pthread_mutex_init(&pool->mutex, NULL);
pthread_cond_init(&pool->cond, NULL);
pthread_mutex_lock(&pool->mutex);
// 2.初始化task_list, tid_info_list
pool->task_list = NULL;
pool->tid_info_list = NULL;
// 3.创建要求个数的线程
if (pthread_num > PTHREAD_MAX)
pthread_num = PTHREAD_MAX;
pool->n_usr_thread = pthread_num; // 用户要求创建的线程个数
pool->n_wait_thread = 0; // 当前正在睡眠的线程个数
pool->n_cur_thread = 0; // 当前存在的线程个数
pool->n_cur_task = 0; // 初始任务个数
if (-1 == create_detach_pthread(pool, pool->n_usr_thread))
return -1;
pthread_mutex_unlock(&pool->mutex);
return 0;
}
/* 成功返回添加后链表中的任务个数, 失败返回-1; */
int add_task(thread_pool_t *pool, task do_task, void *arg)
{
task_t *tmp = pool->task_list; // 链表头
// 构建一个新任务
task_t *new_task = malloc(sizeof(task_t));
new_task->do_task = do_task;
new_task->arg = arg;
pthread_mutex_lock(&pool->mutex);
// 1.将任务投递到链表尾部
if (NULL == tmp) { // 链表为空
pool->task_list = new_task;
pool->task_list->next = NULL;
}
else {
while(tmp && tmp->next)
tmp = tmp->next;
tmp->next = new_task; // tmp是链表的最后一项
tmp->next->next = NULL;
}
pool->n_cur_task++; // 当前存在的任务个数
pr("add_task: n_cur_task = %d\n", pool->n_cur_task);
// cond上有线程正在睡眠
if (pool->n_wait_thread > 0)
pthread_cond_broadcast(&pool->cond); // 广播所有睡眠在cond上的线程
// cond上没有线程在睡眠
else {
if (pool->n_cur_thread < PTHREAD_MAX) { // 如果没有超过最大线程数, 创建线程来执行
if(-1 == create_detach_pthread(pool, 1))
return -1;
pthread_cond_broadcast(&pool->cond); // 广播创建好的线程来执行该任务(其他线程不会抢到这个任务)
}
// 线程数 = PTHREAD_MAX, 等待线程顺序执行任务
}
pthread_mutex_unlock(&pool->mutex);
return 0;
}
/* 销毁线程池 */
int destory_pool(thread_pool_t *pool)
{
tid_info_t *head = pool->tid_info_list;
task_t *head2 = pool->task_list;
// 删除tid_info_list及取消对应的线程
while(head) {
pthread_cancel(head->tid); // 取消线程
if (head->next)
pool->tid_info_list = head->next; // 从链表中删除
else
pool->tid_info_list = NULL;
free(head); // 释放空间
head = pool->tid_info_list;
}
// 删除task_list
while(head2) {
if (head2->next)
pool->task_list = head2->next; // 从链表中删除
else
pool->task_list = NULL;
free(head2); // 释放空间
head2 = pool->task_list;
}
pthread_mutex_destroy(&pool->mutex);
pthread_cond_destroy(&pool->cond);
return 0;
}
测试程序: test.c
#include "my_pthread_pool.h"
void *do_task(void *arg)
{
printf("%s\n", (char *)arg);
sleep(1);
}
int main(int argc, char **argv)
{
thread_pool_t pool;
init_pool(&pool, 5);
while(getchar()) {
add_task(&pool,do_task, "do_task"); // "do_task"保存在全局数据段
}
//printf("n_cur_task = %d\n", pool.n_cur_task);
destory_pool(&pool);
return 0;
}
(5) 执行结果及分析
/* */
book@gui_hua_shu:~/test$ ./a.out
n_wait_thread = 1 // 初始创建了5个线程
n_wait_thread = 2
n_wait_thread = 3
n_wait_thread = 4
n_wait_thread = 5
add_task: n_cur_task = 1 // 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 4 // 占用1个线程
task_list: = 0
npthread: = 5
do_task // sleep(1)
add_task: n_cur_task = 1 // 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 3 // 占用2个线程
task_list: = 0
npthread: = 5
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 2 // 占用3个线程
task_list: = 0
npthread: = 5
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 1 // 占用4个线程
task_list: = 0
npthread: = 5
do_task
// 占用5个线程
add_task: n_cur_task = 1 // 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 0 // 占用5个线程
task_list: = 0
npthread: = 5
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:6个
task_list: = 0
npthread: = 6
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:7个
task_list: = 0
npthread: = 7
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:8个
task_list: = 0
npthread: = 8
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:9个
task_list: = 0
npthread: = 9
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:10个
task_list: = 0
npthread: = 10
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:11个
task_list: = 0
npthread: = 11
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:12个
task_list: = 0
npthread: = 12
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:13个
task_list: = 0
npthread: = 13
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:14个
task_list: = 0
npthread: = 14
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:15个
task_list: = 0
npthread: = 15
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:16个
task_list: = 0
npthread: = 16
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:17个
task_list: = 0
npthread: = 17
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:18个
task_list: = 0
npthread: = 18
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:19个
task_list: = 0
npthread: = 19
do_task
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:20个
task_list: = 0
npthread: = 20
do_task
// 创建线程达到了上线20个, 让任务排队等待执行, 不再创建新线程来执行
add_task: n_cur_task = 1 // 添加了1个任务, 链表中还有1个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 2 // 添加了1个任务, 链表中还有2个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 3 // 添加了1个任务, 链表中还有3个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 4 // 添加了1个任务, 链表中还有4个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 5 // 添加了1个任务, 链表中还有5个任务待处理, 线程数为最大值:20个
task_list: = 5 // 处理了1个任务, 链表中还有5个任务待处理, 线程数为最大值:20个
npthread: = 20
do_task
task_list: = 4 // 处理了一个任务, 链表中还有4个任务待处理, 线程数为最大值:20个
npthread: = 20
do_task
task_list: = 3 // 处理了一个任务, 链表中还有3个任务待处理, 线程数为最大值:20个
npthread: = 20
do_task
task_list: = 2 // 处理了一个任务, 链表中还有2个任务待处理, 线程数为最大值:20个
npthread: = 20
do_task
task_list: = 1 // 处理了一个任务, 链表中还有1个任务待处理, 线程数为最大值:20个
npthread: = 20
do_task
task_list: = 0 // 处理了一个任务, 链表中没有任务待处理,但是所有线程都被占用, 线程数为最大值:20个
npthread: = 20
do_task
cancel: n_cur_thread = 19 // 第20个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 18 // 第19个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 17 // 第18个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 16 // 第17个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 15 // 第16个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 14 // 第15个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 13 // 第14个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 12 // 第13个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 11 // 第12个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 10 // 第11个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 9 // 第10个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 8 // 第9个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 7 // 第8个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 6 // 第7个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 5 // 第6个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
// 初始创建的线程不关闭
n_wait_thread = 1 //第5个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 2 //第4个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 3 //第3个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 4 //第2个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 5 //第1个线程执行完了, 任务链表没有待执行任务, 该线程休眠