IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言线程池 -> 正文阅读

[C++知识库]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;
}

总结

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

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-26 15:13:27  更:2022-05-26 15:13:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 20:40:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码