21.线程池

21.线程池

什么是线程池?

是一个抽象的概念,若干个线程组合到一起,形成线程池。

为什么需要线程池?

多线程版服务器一个客户端就需要创建一个线程!若客户端太多,显然不太合适。

什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁地创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。

实现的时候类似于生产者和消费者。

线程池和任务池:

任务池相当于共享资源,所以需要使用互斥锁,当任务池中没有任务的时候需要让线程阻塞,所以需要使用条件变量。

如何让线程执行不同的任务?

使用回到函数,在任务中设置任务执行函数,这样可以起到不同的任务执行不同的函数。

通过阅读线程池代码思考如下问题?

▶熟悉结构体 threadpool_t

▶线程池如何创建起来? 各种初始化,malloc,pthread_create,pthread_cond_init pthread_mutex_init

▶线程池内都有几类线程? 2类:管理线程+工作线程

▶管理者线程的任务是什么?任务如何实现? 任务是添加线程或者删除线程,通过2个算法,删除线程 wait_exit_thr_num = 10

▶工作线程如何工作? 等待有任务,抢到任务,修改busy_thr_num ++ 执行任务 修改 busy_thr_num --

▶线程池是如何销毁的? 自爆shutdown 诱杀!

threadpoolsimple.h

#ifndef _THREADPOOL_H
#define _THREADPOOL_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

typedef struct _PoolTask
{
    int tasknum;//模拟任务编号
    void *arg;//回调函数参数
    void (*task_func)(void *arg);//任务的回调函数
}PoolTask ;

typedef struct _ThreadPool
{
    int max_job_num;//最大任务个数
    int job_num;//实际任务个数
    PoolTask *tasks;//任务队列数组
    int job_push;//入队位置
    int job_pop;// 出队位置

    int thr_num;//线程池内线程个数
    pthread_t *threads;//线程池内线程数组
    int shutdown;//是否关闭线程池
    pthread_mutex_t pool_lock;//线程池的锁
    pthread_cond_t empty_task;//任务队列为空的条件
    pthread_cond_t not_empty_task;//任务队列不为空的条件

}ThreadPool;

void create_threadpool(int thrnum,int maxtasknum);//创建线程池--thrnum  代表线程个数,maxtasknum 最大任务个数
void destroy_threadpool(ThreadPool *pool);//摧毁线程池	
void addtask(ThreadPool *pool);//添加任务到线程池
void taskRun(void *arg);//任务回调函数

#endif

threadpoolsimple.c

//简易版线程池
#include "threadpoolsimple.h"

ThreadPool* thrPool = NULL;

int beginnum = 1000;

void* thrRun(void* arg)
{
    //printf("begin call %s-----\n",__FUNCTION__);
    ThreadPool* pool = (ThreadPool*)arg;
    int taskpos = 0;//任务位置
    PoolTask* task = (PoolTask*)malloc(sizeof(PoolTask));

    while (1)
    {
        //获取任务,先要尝试加锁
        pthread_mutex_lock(&thrPool->pool_lock);

        //无任务并且线程池不是要摧毁
        while (thrPool->job_num <= 0 && !thrPool->shutdown)
        {
            //如果没有任务,线程会阻塞
            pthread_cond_wait(&thrPool->not_empty_task, &thrPool->pool_lock);
        }

        if (thrPool->job_num)
        {
            //有任务需要处理
            taskpos = (thrPool->job_pop++) % thrPool->max_job_num;
            //printf("task out %d...tasknum===%d tid=%lu\n",taskpos,thrPool->tasks[taskpos].tasknum,pthread_self());
            //为什么要拷贝?避免任务被修改,生产者会添加任务
            memcpy(task, &thrPool->tasks[taskpos], sizeof(PoolTask));
            task->arg = task;
            thrPool->job_num--;
            //task = &thrPool->tasks[taskpos];
            pthread_cond_signal(&thrPool->empty_task);//通知生产者
        }

        if (thrPool->shutdown)
        {
            //代表要摧毁线程池,此时线程退出即可
            //pthread_detach(pthread_self());//临死前分家
            pthread_mutex_unlock(&thrPool->pool_lock);
            free(task);
            pthread_exit(NULL);
        }

        //释放锁
        pthread_mutex_unlock(&thrPool->pool_lock);
        task->task_func(task->arg);//执行回调函数
    }

    //printf("end call %s-----\n",__FUNCTION__);
}

//创建线程池
void create_threadpool(int thrnum, int maxtasknum)
{
    printf("begin call %s-----\n", __FUNCTION__);
    thrPool = (ThreadPool*)malloc(sizeof(ThreadPool));

    thrPool->thr_num = thrnum;
    thrPool->max_job_num = maxtasknum;
    thrPool->shutdown = 0;//是否摧毁线程池,1代表摧毁
    thrPool->job_push = 0;//任务队列添加的位置
    thrPool->job_pop = 0;//任务队列出队的位置
    thrPool->job_num = 0;//初始化的任务个数为0

    thrPool->tasks = (PoolTask*)malloc((sizeof(PoolTask) * maxtasknum));//申请最大的任务队列

    //初始化锁和条件变量
    pthread_mutex_init(&thrPool->pool_lock, NULL);
    pthread_cond_init(&thrPool->empty_task, NULL);
    pthread_cond_init(&thrPool->not_empty_task, NULL);

    int i = 0;
    thrPool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thrnum);//申请n个线程id的空间

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    for (i = 0; i < thrnum; i++)
    {
        pthread_create(&thrPool->threads[i], &attr, thrRun, (void*)thrPool);//创建多个线程
    }
    //printf("end call %s-----\n",__FUNCTION__);
}
//摧毁线程池
void destroy_threadpool(ThreadPool* pool)
{
    pool->shutdown = 1;//开始自爆
    pthread_cond_broadcast(&pool->not_empty_task);//诱杀 

    int i = 0;
    for (i = 0; i < pool->thr_num; i++)
    {
        pthread_join(pool->threads[i], NULL);
    }

    pthread_cond_destroy(&pool->not_empty_task);
    pthread_cond_destroy(&pool->empty_task);
    pthread_mutex_destroy(&pool->pool_lock);

    free(pool->tasks);
    free(pool->threads);
    free(pool);
}

//添加任务到线程池
void addtask(ThreadPool* pool)
{
    //printf("begin call %s-----\n",__FUNCTION__);
    pthread_mutex_lock(&pool->pool_lock);

    //实际任务总数大于最大任务个数则阻塞等待(等待任务被处理)
    while (pool->max_job_num <= pool->job_num)
    {
        pthread_cond_wait(&pool->empty_task, &pool->pool_lock);
    }

    int taskpos = (pool->job_push++) % pool->max_job_num;
    //printf("add task %d  tasknum===%d\n",taskpos,beginnum);
    pool->tasks[taskpos].tasknum = beginnum++;
    pool->tasks[taskpos].arg = (void*)&pool->tasks[taskpos];
    pool->tasks[taskpos].task_func = taskRun;
    pool->job_num++;

    pthread_mutex_unlock(&pool->pool_lock);

    pthread_cond_signal(&pool->not_empty_task);//通知包身工
    //printf("end call %s-----\n",__FUNCTION__);
}

//任务回调函数
void taskRun(void* arg)
{
    PoolTask* task = (PoolTask*)arg;
    int num = task->tasknum;
    printf("task %d is runing %lu\n", num, pthread_self());

    sleep(1);
    printf("task %d is done %lu\n", num, pthread_self());
}

int main()
{
    create_threadpool(3, 20);
    int i = 0;
    for (i = 0; i < 50; i++)
    {
        addtask(thrPool);//模拟添加任务
    }

    sleep(20);
    destroy_threadpool(thrPool);

    return 0;
}
posted @   CodeMagicianT  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示