【网络编程】线程池并发服务器

线程池思路

任务队列作为一个缓冲区,线程池中的线程抢任务。
在这里插入图片描述
threadpool_t结构体

struct threadpool_t {
    pthread_mutex_t lock; 
    pthread_mutex_t thread_counter; //busy_thread_num修改时加解锁
    pthread_cond_t queue_not_full;
    pthread_cond_t queue_not_empty;

    pthread_t *thread; //线程数组
    pthread_t adjust_thread; //管理者线程管理线程池的线程数量
    task_queue_t *task_queue; //任务队列数组

    int min_thread_num;
    int max_thread_num;
    int live_thread_num;
    int busy_thread_num;
    int wait_exit_thread_num;  //等待销毁的线程数

    int queue_front;
    int queue_rear;
    int queue_size; 
    int queue_max_size;

    int shutdown; //shutdown == 0 线程池开,shutdown == 1线程池关
};

task_queue_t结构体

typedef struct { 
    void *(*function)(void *); //回调函数
    void *arg; //参数
} task_queue_t; //任务

main()函数

  1. threadpool_create() 初始化线程池
  2. threadpool_add()向任务队列添加任务
  3. threadpool_destory()
int main() {
    struct threadpool_t *threadpool = threadpool_create(3, 100, 100); //min_thread_num, max_thread_num, queue_max_size
    intptr_t nums[80];
    for (intptr_t i = 0; i < 80; i++) {
        nums[i] = i; //task;
        threadpool_add(threadpool, process, (void *)nums[i]); // 线程池,回调函数,泛型参数
    }
    sleep(10);
    threadpool_destory(threadpool);
    return 0;
}

threadpool_create() 函数初始化threadpool_t

  1. int类型的变量赋值
  2. 初始化锁和条件变量
  3. mallocmax_thread_numthread
  4. min_thread_num个线程创建线程pthread_create(),调用func_thread函数
  5. 为管理者线程创建线程,调用func_adjust_thread函数
  6. mallocqueue_max_sizetask_queue_t
threadpool_t *threadpool_create(int min_thread_num, int max_thread_num, int queue_max_size){
    struct threadpool_t *pool = (threadpool_t *)malloc(sizeof(threadpool_t));
    do{
        if (pool == NULL) {
            printf("pool malloc failed");
            break; 
        }
		// 为int类型的变量赋值
		pool->min_thread_num = min_thread_num; 
        pool->max_thread_num = max_thread_num;
        pool->live_thread_num = min_thread_num;
        pool->busy_thread_num = 0;
        pool->wait_exit_thread_num = 0;

        pool->queue_front = 0;
        pool->queue_rear = 0;
        pool->queue_size = 0;
        pool->queue_max_size = queue_max_size;

        pool->shutdown = 0;
        //初始化锁和条件变量
        if (pthread_mutex_init(&(pool->lock), NULL) != 0 ||
            pthread_mutex_init(&(pool->thread_counter), NULL) != 0 ||
            pthread_cond_init(&(pool->queue_not_full), NULL) != 0 ||
            pthread_cond_init(&(pool->queue_not_empty), NULL) != 0 ) {
                printf("init the lock or cond failed");
                break; 
        }
        //malloc出max_thread_num个thread
        pool->thread = (pthread_t *)malloc(sizeof(pthread_t) * max_thread_num);
        if (pool->thread == NULL) {
            printf("thread malloc failed");
            break; 
        }
        //赋0
        memset(pool->thread, 0, sizeof(pthread_t) * max_thread_num);
        //为min_thread_num个线程创建线程pthread_create(),调用func_thread函数
        for (int i = 0; i < min_thread_num; i++) {
            pthread_create(&(pool->thread[i]), NULL, func_thread, (void *)pool);
            printf("start thread %ld.\n", pool->thread[i]);
        }
        //为管理者线程创建线程,调用func_adjust函数
        pthread_create(&(pool->adjust_thread), NULL, func_adjust_thread, (void *)pool);
		//malloc出queue_max_size个task_queue_t
        pool->task_queue = (task_queue_t *)malloc(sizeof(task_queue_t) * (pool->queue_max_size));
        if (pool->task_queue == NULL) {
            printf("task_queue malloc failed");
            break; 
        }
		return pool;
    } while(0); // 只执行一次
    
    threadpool_free(pool); // 若前面代码调用失败,free整个空间
    return NULL;
}

threadpool_add()函数向任务队列中添加任务

  1. 添加任务
  2. 发出任务队列不为空的信号
int threadpool_add(threadpool_t *pool, void *(*function)(void *), void *arg) {
	pthread_mutex_lock(&(pool->lock));
	if (pool->shutdown == 1) { 
		pthread_mutex_unlock(&(pool->lock));
		return -1;
	}
	while(pool->queue_size == pool->queue_max_size) {
		pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
	}
	if (pool->task_queue[pool->queue_rear].arg != NULL) { //防止内存泄露
		free(pool->task_queue[pool->queue_rear].arg);
		pool->task_queue[pool->queue_rear].arg = NULL;
	}
	pool->task_queue[pool->queue_rear].function = function;
	pool->task_queue[pool->queue_rear].arg = arg;
	pool->queue_rear = (pool->queue_rear + 1) % (pool->queue_max_size);
	pool->queue_size++;
	pthread_cond_signal(&(pool->queue_not_empty));
	pthread_mutex_unlock(&(pool->lock));
	return 0;
}

func_thread():普通线程的回调函数

适用场景

  • 线程池初始化时创建min_thread_num个线程
  • 在管理者线程的回调函数中当需要增加线程池线程数量时
  • shutdown == 1 或者 wait_exit_thread_num > 0需要退出线程时,唤醒queue_not_empty,执行pthread_exit(NULL)

流程

  1. 将一个任务出队
  2. 将任务放入线程池中执行,这一步比较抽象,转换成代码就是busy_thread_num++,同时前后加解锁
  3. 任务执行完出线程池,转换成代码就是busy_thread_num--,同时前后加解锁
void* func_thread(void *arg) { //
    threadpool_t *pool = (threadpool_t *)arg;
    task_queue_t task;

    while(1) {
        pthread_mutex_lock(&(pool->lock));

        while ((pool->queue_size) == 0 && (pool->shutdown) == 0) { //task_queue为空
            printf("thread %ld is waiting.\n", pthread_self());
            pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));

            if (pool->wait_exit_thread_num > 0) { //shudown == 0时结束线程直到只剩最少的线程数
                pool->wait_exit_thread_num--;
                if (pool->live_thread_num > pool->min_thread_num) {
                    printf("thread %ld is exiting.\n", pthread_self());
                    pool->live_thread_num--;
                    pthread_mutex_unlock(&(pool->lock));
                    pthread_exit(NULL); // 先解锁再exit
                }
            }
        }
        while(pool->shutdown) { //shutdown == 1时结束全部线程
            pthread_mutex_unlock(&(pool->lock));
            printf("thread %ld is exiting.\n", pthread_self());
            pthread_exit(NULL);
        }
        //出队
        task.function = pool->task_queue[pool->queue_front].function;
        task.arg = pool->task_queue[pool->queue_front].arg;
        pool->queue_front = (pool->queue_front + 1) % (pool->queue_max_size); // 环形队列
        pool->queue_size--;
        pthread_cond_broadcast(&(pool->queue_not_full));
        pthread_mutex_unlock(&(pool->lock));
        //入线程池
        printf("thread %ld is start working.\n",pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thread_num++;
        pthread_mutex_unlock(&(pool->thread_counter));
        task.function(task.arg); //
        //处理完任务
        printf("thread %ld is end working.\n", pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thread_num--;
        pthread_mutex_unlock(&(pool->thread_counter)); 
    }
    pthread_exit(NULL);
}

func_adjust_thread:管理者线程的回调函数

  1. 何时添加线程池中的线程
  2. 何时减少线程池中的线程。这里减少线程池中的线程的方法很有意思。即先增加wait_exit_thread_num,再唤醒queue_not_empty
void* func_adjust_thread(void* arg) {
    threadpool_t *pool = (threadpool_t *)arg;

    //ques:不进行值的修改为何还需要锁

    while(pool->shutdown == 0) { //线程池未关闭

        sleep(DEFAULT_TIME);

        pthread_mutex_lock(&(pool->lock));
        int live_thread_num = pool->live_thread_num;
        int max_thread_num = pool->max_thread_num;
        int min_thread_num = pool->min_thread_num;
        int queue_size = pool->queue_size;
        pthread_mutex_unlock(&(pool->lock));

        pthread_mutex_lock(&(pool->thread_counter));
        int busy_thread_num = pool->live_thread_num;
        pthread_mutex_unlock(&(pool->thread_counter));

        //add
        if (queue_size > MIN_WAIT_TASK_NUM && live_thread_num < max_thread_num) {
            int add = 0;
            pthread_mutex_lock(&(pool->lock));
            for (int i = 0; (i < max_thread_num) && (add < DEFAULT_STEP) && (live_thread_num < max_thread_num); i++) { //因为并发,所以这里也需要保证<max_thread_num
                if (pool->thread[i] == 0 ) {
                    pthread_create(&(pool->thread[i]), NULL, func_thread, (void*)pool);
                    add++;
                    pool->live_thread_num++; // don't forget
                }
            }
            pthread_mutex_unlock(&(pool->lock));
        }

        //sub
        if (live_thread_num > (2 * busy_thread_num) && live_thread_num > min_thread_num) {
            pthread_mutex_lock(&(pool->lock));
            pool->wait_exit_thread_num = DEFAULT_STEP; // 修改的时候一定要加锁
            pthread_mutex_unlock(&(pool->lock));

            for (int i = 0; i < DEFAULT_STEP; i++) {
                pthread_cond_signal(&(pool->queue_not_empty));
            }
        }
    }
    return NULL;
}

完整代码

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>

#define DEFAULT_STEP 10  
#define MIN_WAIT_TASK_NUM 10
#define DEFAULT_TIME 10

typedef struct { 
    void *(*function)(void *); //回调函数
    void *arg; //参数
} task_queue_t; //任务

struct threadpool_t {
    pthread_mutex_t lock; 
    pthread_mutex_t thread_counter; //busy_thread_num修改时加解锁
    pthread_cond_t queue_not_full;
    pthread_cond_t queue_not_empty;

    pthread_t *thread; //线程数组
    pthread_t adjust_thread; //管理者线程管理线程池的线程数量
    task_queue_t *task_queue; //任务队列数组

    int min_thread_num;
    int max_thread_num;
    int live_thread_num;
    int busy_thread_num;
    int wait_exit_thread_num;  //等待销毁的线程数

    int queue_front;
    int queue_rear;
    int queue_size; 
    int queue_max_size;

    int shutdown; //shutdown == 0 线程池开,shutdown == 1线程池关
};

int threadpool_free(threadpool_t *pool);
int threadpool_destory(threadpool_t *pool);

void* func_thread(void *arg) { //
    threadpool_t *pool = (threadpool_t *)arg;
    task_queue_t task;

    while(1) {
        pthread_mutex_lock(&(pool->lock));

        while ((pool->queue_size) == 0 && (pool->shutdown) == 0) { //task_queue为空
            printf("thread %ld is waiting.\n", pthread_self());
            pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));

            if (pool->wait_exit_thread_num > 0) { //shudown == 0时结束线程直到只剩最少的线程数
                pool->wait_exit_thread_num--;
                if (pool->live_thread_num > pool->min_thread_num) {
                    printf("thread %ld is exiting.\n", pthread_self());
                    pool->live_thread_num--;
                    pthread_mutex_unlock(&(pool->lock));
                    pthread_exit(NULL); // 先解锁再exit
                }
            }
        }

        while(pool->shutdown) { //shutdown == 1时结束全部线程
            pthread_mutex_unlock(&(pool->lock));
            printf("thread %ld is exiting.\n", pthread_self());
            pthread_exit(NULL);
        }

        //出队
        task.function = pool->task_queue[pool->queue_front].function;
        task.arg = pool->task_queue[pool->queue_front].arg;
        pool->queue_front = (pool->queue_front + 1) % (pool->queue_max_size); // 环形队列
        pool->queue_size--;
        pthread_cond_broadcast(&(pool->queue_not_full));
        pthread_mutex_unlock(&(pool->lock));
        
        //入线程池
        printf("thread %ld is start working.\n",pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thread_num++;
        pthread_mutex_unlock(&(pool->thread_counter));
        task.function(task.arg); //
        
        //处理完任务
        printf("thread %ld is end working.\n", pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thread_num--;
        pthread_mutex_unlock(&(pool->thread_counter)); 
    }
    pthread_exit(NULL);
}

void* func_adjust_thread(void* arg) {
    threadpool_t *pool = (threadpool_t *)arg;

    //ques:不进行值的修改为何还需要锁

    while(pool->shutdown == 0) { //线程池未关闭

        sleep(DEFAULT_TIME);

        pthread_mutex_lock(&(pool->lock));
        int live_thread_num = pool->live_thread_num;
        int max_thread_num = pool->max_thread_num;
        int min_thread_num = pool->min_thread_num;
        int queue_size = pool->queue_size;
        pthread_mutex_unlock(&(pool->lock));

        pthread_mutex_lock(&(pool->thread_counter));
        int busy_thread_num = pool->live_thread_num;
        pthread_mutex_unlock(&(pool->thread_counter));

        //add
        if (queue_size > MIN_WAIT_TASK_NUM && live_thread_num < max_thread_num) {
            int add = 0;
            pthread_mutex_lock(&(pool->lock));
            for (int i = 0; (i < max_thread_num) && (add < DEFAULT_STEP) && (live_thread_num < max_thread_num); i++) { //因为并发,所以这里也需要保证<max_thread_num
                if (pool->thread[i] == 0 ) {
                    pthread_create(&(pool->thread[i]), NULL, func_thread, (void*)pool);
                    add++;
                    pool->live_thread_num++; // don't forget
                }
            }
            pthread_mutex_unlock(&(pool->lock));
        }

        //sub
        if (live_thread_num > (2 * busy_thread_num) && live_thread_num > min_thread_num) {
            pthread_mutex_lock(&(pool->lock));
            pool->wait_exit_thread_num = DEFAULT_STEP; // 修改的时候一定要加锁
            pthread_mutex_unlock(&(pool->lock));

            for (int i = 0; i < DEFAULT_STEP; i++) {
                pthread_cond_signal(&(pool->queue_not_empty));
            }
        }
    }
    return NULL;
}

threadpool_t *threadpool_create(int min_thread_num, int max_thread_num, int queue_max_size){
    struct threadpool_t *pool = (threadpool_t *)malloc(sizeof(threadpool_t));
    do{
        if (pool == NULL) {
            printf("pool malloc failed");
            break; 
        }
		
		pool->min_thread_num = min_thread_num; 
        pool->max_thread_num = max_thread_num;
        pool->live_thread_num = min_thread_num;
        pool->busy_thread_num = 0;
        pool->wait_exit_thread_num = 0;

        pool->queue_front = 0;
        pool->queue_rear = 0;
        pool->queue_size = 0;
        pool->queue_max_size = queue_max_size;

        pool->shutdown = 0;
        
        if (pthread_mutex_init(&(pool->lock), NULL) != 0 ||
            pthread_mutex_init(&(pool->thread_counter), NULL) != 0 ||
            pthread_cond_init(&(pool->queue_not_full), NULL) != 0 ||
            pthread_cond_init(&(pool->queue_not_empty), NULL) != 0 ) {
                printf("init the lock or cond failed");
                break; 
        }
        pool->thread = (pthread_t *)malloc(sizeof(pthread_t) * max_thread_num);
        if (pool->thread == NULL) {
            printf("thread malloc failed");
            break; 
        }
        
        memset(pool->thread, 0, sizeof(pthread_t) * max_thread_num);
        for (int i = 0; i < min_thread_num; i++) {
            pthread_create(&(pool->thread[i]), NULL, func_thread, (void *)pool);
            printf("start thread %ld.\n", pool->thread[i]);
        }
        pthread_create(&(pool->adjust_thread), NULL, func_adjust_thread, (void *)pool);

        pool->task_queue = (task_queue_t *)malloc(sizeof(task_queue_t) * (pool->queue_max_size));
        if (pool->task_queue == NULL) {
            printf("task_queue malloc failed");
            break; 
        }


		return pool;
    } while(0); // 只执行一次
    
    threadpool_free(pool); // 若前面代码调用失败,free整个空间
    return NULL;
}

int threadpool_add(threadpool_t *pool, void *(*function)(void *), void *arg) {
	pthread_mutex_lock(&(pool->lock));
	if (pool->shutdown == 1) {
		pthread_mutex_unlock(&(pool->lock));
		return -1;
	}
	while(pool->queue_size == pool->queue_max_size) {
		pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
	}
	if (pool->task_queue[pool->queue_rear].arg != NULL) { //important 
		free(pool->task_queue[pool->queue_rear].arg);
		pool->task_queue[pool->queue_rear].arg = NULL;
	}
	pool->task_queue[pool->queue_rear].function = function;
	pool->task_queue[pool->queue_rear].arg = arg;
	pool->queue_rear = (pool->queue_rear + 1) % (pool->queue_max_size);
	pool->queue_size++;
	pthread_cond_signal(&(pool->queue_not_empty));
	pthread_mutex_unlock(&(pool->lock));
	return 0;
}

int threadpool_free(threadpool_t *pool) {
    if (pool == NULL) return -1;
    if (pool->task_queue) {
        free(pool->task_queue);
    }
    if (pool->thread) {
        free(pool->thread);
    }
    pthread_mutex_lock(&(pool->lock));
    pthread_mutex_destroy(&(pool->lock));
    pthread_mutex_lock(&(pool->thread_counter));
    pthread_mutex_destroy(&(pool->thread_counter));
    pthread_cond_destroy(&(pool->queue_not_empty));
    pthread_cond_destroy(&(pool->queue_not_full));
    free(pool); // free释放了一个结构体,但是其中的指针还可以访问
    pool = NULL;
    return 0;
}

int threadpool_destory(threadpool_t *pool) { //此函数为什么不加锁
    if (pool == NULL) return -1;
    pool->shutdown = 1;
    pthread_join(pool->adjust_thread, NULL);
    for (int i = 0; i < pool->live_thread_num; i++) {
        pthread_cond_broadcast(&(pool->queue_not_empty));
    }
    for (int i = 0; i < pool->live_thread_num; i++) { // live_thread_num <= min_thread_num时???
        pthread_join(pool->thread[i], NULL);
    }
    threadpool_free(pool);
    return 0;
}

void* process (void *arg) {
    printf("thread %ld is working %ld task.\n", pthread_self(), (intptr_t)arg);
    sleep(1);
    printf("task %ld is end.\n", (intptr_t)arg);
    return NULL;
}
int main() {
    struct threadpool_t *threadpool = threadpool_create(3, 100, 100);
    printf("threadpool init.\n");
	
    intptr_t nums[80];
    for (intptr_t i = 0; i < 80; i++) {
        nums[i] = i; //task;
        printf("add task %ld\n", nums[i]);
        threadpool_add(threadpool, process, (void *)nums[i]); // 线程池,回调函数,泛型参数
    }
    sleep(10);
    threadpool_destory(threadpool);

    return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值