C语言线程池

什么是线程池

线程池就是一个容纳多个线程的容器,对于一线线程我们可以多次对此线程进行重复使用,从而省去频繁创建线程对象的操作。

为什么要使用线程池

频繁的进行进程的创建与销毁将带来很多开销。不但如此,进程间频繁的切换也将减低 CPU 的利用率。 如果能复用之前创建的进程,而不是为每个并发任务创建一个进程,能有效降低进程创建与销毁的开销并减少进程间切换,从而减少对 CPU 资源的浪费。 虽然线程创建与销毁的代价小于进程创建与销毁,隶属同一进程的线程间切换的代价也小于进程间切换,但复用之前创建的线程,也能有效降低线程创建与销毁的开销并减少线程间切换,从而减少对 CPU 资源的浪费。
当任务很多的时,我们就可以调用线程池,从而有效的的对CPU资源的浪费。
图解:
在这里插入图片描述

线程池的工作流程图

在这里插入图片描述

任务队列

我们应该以任务队列的形式存储任务,线程池中的线程只需要从任务队列中拿任务就可以了

typedef struct task
{
    void(*run)(void*arg);//函数指针,指向一个函数(任务)
    void*arg;            //上面指针所指向函数的参数
    struct task *next;  //next指针,指向下一个任务节点
}task;

这样我们就把任务队列创建好了。

线程池的创建

typedef struct threadpool
{
    task*first;   //指向任务队列的起始位置
    task*end;     //指向任务队列的末尾位置
    int threadNUM;//线程数量
    int tasksize;//任务数量
    pthread_mutex_t mutexpool;//锁整个线程池
    pthread_cond_t notempty;//任务队列是不是空
    int shutdown;//是不是要销毁线程池,销毁为1,不销毁为0
}threadpool;

mutexpool是一个互斥锁,他的作用就是,当某一个线程需要拿任务或者要修改线程池中的某个成员时,为了防止其他线程同时也在对线程池进行访问,从而导致数据错乱,我们就需要使用互斥锁,让某个线程在访问线程池的时候将线程池锁住,不让其他线程进行访问。
notempty是一个条件变量,其作用就是当任队列为空时,我们就需要拿该条件变量将线程阻塞,当有新任务添加进来时,再对线程进行唤醒。

此时我们已经创建出了线程池,我们接下来所需要实现的接口。

//初始化线程池
threadpool*threadpoolinit(int nmberu);
//销毁线程池
int threadpooldestroy(threadpool*pool);
//给线程池添加任务
void threadpoolAdd(threadpool*pool,void(*run)(void*),void*arg);
//工作者(实现任务的实现)
void*worker(void*arg);

线程池的初始化

我们所要完成的任务就是,使用malloc函数将线程在堆区创建出来,然后对线程池中的互斥锁和条件变量进行初始化时。
然后对一些成员附上初始值。
线程池初始化时,还有很重要的一步,就是创建线程。
代码如下:

threadpool *threadpoolinit(int number)
{
    threadpool *pool = (threadpool *)malloc(sizeof(threadpool));
    if (pool == NULL)
    {
        printf("malloc error\n");
        return NULL;
    }
    pool->threadNUM = number;
    pool->tasksize=0;
    pool->first = NULL;
    pool->end = NULL;
    //锁和条件变量初化
    pthread_mutex_init(&pool->mutexpool, NULL);
    pthread_cond_init(&pool->notempty, NULL);
    pool->shutdown=0;
    //创建线程
    for (int i = 0; i < number; i++)
    {
        pthread_t tid;
        pthread_create(&tid, NULL, worker, pool);
    }
    return pool;
}

工作者

工作者所担任的任务就是,执行任务。当没有任务时,线程就进入阻塞状态。

void *worker(void *arg)
{
    threadpool *pool = (threadpool *)arg;
    while (1)
    {
        pthread_mutex_lock(&pool->mutexpool);
        //当前任务队列是否为空
        while (pool->first == NULL && !pool->shutdown)
        {
            //阻塞工作线程
            pthread_cond_wait(&pool->notempty, &pool->mutexpool);
        }
        //判断当前线程池是否关闭
        if(pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexpool);
            pthread_exit(NULL);
        }
        //从任务队列中取出任务
        task *t = pool->first;
        pool->first = t->next;
        pool->tasksize--;
        pthread_mutex_unlock(&pool->mutexpool);//i解锁让其他线程去执行其他任务
        t->run(t->arg);
        free(t);
        t=NULL; 
    }
    return NULL;
}

当线程池收到销毁命令时,也就是pool->shutdown==1(后面会有对应接口),解锁(防止进入死锁),然后用pthread_exit函数进行终止线程。

添加任务

void threadpoolAdd(threadpool*pool,void(*run)(void*),void*arg)
{
    if(pool->shutdown)//线程池已经被关闭
    {
        return;
    }
    task*t=(task*)malloc(sizeof(task));
    t->arg=arg;
    t->run=run;
    t->next=NULL;
    pthread_mutex_lock(&pool->mutexpool);
    if(pool->first==NULL)//第一个任务
    {
        pool->first=t;
        pool->end=t;
    }
    else
    {
        pool->end->next=t;
        pool->end=t;
    }
    pool->tasksize++;
    pthread_cond_signal(&pool->notempty);//唤醒阻塞的线程
    pthread_mutex_unlock(&pool->mutexpool);
}

线程池的销毁

线程池中下线程的销毁,并不是在该函数中销毁的,而是给线程一个信号,也就是 pool->shutdown=1,然后让线程自己销毁

int threadpooldestroy(threadpool*pool)
{
    if(pool==NULL)
    {
        return -1;
    }
    pool->shutdown=1;//关闭线程池
    //唤醒阻塞的消费者线程顺便销毁
    for(int i=0;i<pool->threadNUM;i++)
    {
        pthread_cond_signal(&pool->notempty);
    }
    //回收锁和条件变量
    pthread_mutex_destroy(&pool->mutexpool);
    pthread_cond_destroy(&pool->notempty);
    //释放线程池
    free(pool);
    pool=NULL;
    return 0;
}

完整代码

threadpool.h

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

//任务队列
typedef struct task
{
    void(*run)(void*arg);
    void*arg;
    struct task *next;
}task;
//线程池
typedef struct threadpool
{
    task*first;
    task*end;
    int threadNUM;//线程数量
    int tasksize;//任务数量
    pthread_mutex_t mutexpool;//锁整个线程池
    pthread_cond_t notempty;//任务队列是不是空
    int shutdown;//是不是要销毁线程池,销毁为1,不销毁为0
}threadpool;
//创建线程池并初始化
threadpool*threadpoolinit(int nmberu);
//销毁线程池
int threadpooldestroy(threadpool*pool);
//给线程池添加任务
void threadpoolAdd(threadpool*pool,void(*run)(void*),void*arg);
void*worker(void*arg);

pthreadpool.c

#include "threadpool.h"
void *worker(void *arg)
{
    threadpool *pool = (threadpool *)arg;
    while (1)
    {
        pthread_mutex_lock(&pool->mutexpool);
        //当前任务队列是否为空
        while (pool->first == NULL && !pool->shutdown)
        {
            //阻塞工作线程
            pthread_cond_wait(&pool->notempty, &pool->mutexpool);
        }
        //判断当前线程池是否关闭
        if(pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexpool);
            pthread_exit(NULL);
        }
        //从任务队列中取出任务
        task *t = pool->first;
        pool->first = t->next;
        pool->tasksize--;
        pthread_mutex_unlock(&pool->mutexpool);//i解锁让其他线程去执行其他任务
        t->run(t->arg);
        free(t);
        t=NULL; 
    }
    return NULL;
}
threadpool *threadpoolinit(int number)
{
    threadpool *pool = (threadpool *)malloc(sizeof(threadpool));
    if (pool == NULL)
    {
        printf("malloc error\n");
        return NULL;
    }
    pool->threadNUM = number;
    pool->tasksize=0;
    pool->first = NULL;
    pool->end = NULL;
    //锁和条件变量初化
    pthread_mutex_init(&pool->mutexpool, NULL);
    pthread_cond_init(&pool->notempty, NULL);
    pool->shutdown=0;
    //创建线程
    for (int i = 0; i < number; i++)
    {
        pthread_t tid;
        pthread_create(&tid, NULL, worker, pool);
    }
    return pool;
}
void threadpoolAdd(threadpool*pool,void(*run)(void*),void*arg)
{
    if(pool->shutdown)//线程池已经被关闭
    {
        return;
    }
    task*t=(task*)malloc(sizeof(task));
    t->arg=arg;
    t->run=run;
    t->next=NULL;
    pthread_mutex_lock(&pool->mutexpool);
    if(pool->first==NULL)//第一个任务
    {
        pool->first=t;
        pool->end=t;
    }
    else
    {
        pool->end->next=t;
        pool->end=t;
    }
    pool->tasksize++;
    pthread_cond_signal(&pool->notempty);//唤醒阻塞的线程
    pthread_mutex_unlock(&pool->mutexpool);
}
int threadpooldestroy(threadpool*pool)
{
    if(pool==NULL)
    {
        return -1;
    }
    pool->shutdown=1;//关闭线程池
    //唤醒阻塞的消费者线程顺便销毁
    for(int i=0;i<pool->threadNUM;i++)
    {
        pthread_cond_signal(&pool->notempty);
    }
    //回收锁和条件变量
    pthread_mutex_destroy(&pool->mutexpool);
    pthread_cond_destroy(&pool->notempty);
    //释放线程池
    free(pool);
    pool=NULL;
    return 0;
}

总结

通过此次任务,学会了线程的创建销毁与使用,学会了互斥锁,条件变量的使用。完成本次任务的过程还算不错,通过查资料,看视频,翻阅大佬们博客,完成本次任务。

  • 17
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

binary~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值