[转帖]线程池实现与讲解

线程,实现,讲解 · 浏览次数 : 0

小编点评

```c# # My Thread Pool Source File using e_pthread_pool; public class E_Pthread_Pool { # Open Interface 1, Adding Task public voidaddTask(Task *task) { if (Stop) return; taskCond.lock(); taskList.push_back(task); taskCond.unlock(); // Remember to unlock cond.lock(); cond.signal(); cond.unlock(); } # Open Interface 2, Removing Task public voiddestroyThread() { printf("destroy\\"); # Forcefully clear list of tasks list_task::iterator it = taskList.begin(); for (; it! = taskList.end(); it++) { Task *t = *it; delete t; t = NULL; } taskList.clear(); # Wait for all threads to finish execution Stop = true; while (count > 0) { cond.lock(); cond.broadcast(); cond.unlock(); sleep(1); } // Summary cleanup Stop = true; while (count > 0) { cond.lock(); cond.broadcast(); cond.unlock(); sleep(1); } } } ```

正文

https://cloud.tencent.com/developer/article/1992928?areaSource=&traceId=

 

文章目录

什么是线程池?为什么要用线程池?

线程池,好东西啊,它有一池子的线程,所以叫线程池。 为什么说它是好东西呢?有的人会觉得,那一池子线程,放在那边又不用,不浪费资源? 其实这笔账很好算的:假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。 当 T1+T3 > T2 && 这种线程被多次调度的时候,你还会觉得浪费资源吗?况且线程池内部又不是缺乏管理,相反,线程池内部管理很严格,吃白饭的线程很难有立足之地,用不上就裁员呗。

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。

线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。

线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目 看一个例子: 假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

线程池的组成部分如下:

1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

线程池的外部支持还有:

5、锁
6、条件变量

这,就是线程池。

示例代码与分析

还是配上代码来讲,不然我自己也晕。

//E_Pthread_Pool.h 我的代码的头文件

#ifndef E_EPOLL_POOL_H
#define E_EPOLL_POOL_H

#include <pthread.h>
#include <unistd.h>
#include <list>	//据说list不安全,不安全就不安全吧,更不安全的都忍了
#include "d_pthread_cond.h"	//封装过的条件变量类,继承自封装的mutex锁类,所以具有锁和条件变量的双重属性

using namespace std;

class Task	//任务接口,每个任务必须实现的接口,以供工作线程调度任务的执行
{
public:
    Task(){}
    virtual ~Task(){}
    virtual int run()=0; //留给子类实现
};

typedef list<Task *> list_task; //任务队列,用于暂存等待处理的任务,等待线程唤醒时处理,提供一种缓冲机制。

class E_PThread_Pool	//线程池类
{
public:
    E_PThread_Pool(unsigned int max=100,unsigned int min=10,unsigned int wait=60);
    ~E_PThread_Pool();
    void addTask(Task *task);	// 往任务队列中添加新线程

private:
    static void *taskThread(void *arg);// 工作线程
    void createThread();		// 新建一个线程
    void destroyThread();		// 销毁一个线程池
    
    unsigned int maxcount;		// 最大线程数
    unsigned int mincount; 		// 最小线程数
    unsigned int count;	 		// 当前线程池中线程数
    unsigned int waitcount; 	// 等待线程数
    unsigned int waitsec;		// 等待时间
    list_task	 taskList;      //任务队列
    D_Pthread_Cond taskCond;    //任务锁,线程接任务时使用
    D_Pthread_Cond cond;        //线程锁,创建线程时使用
    bool Stop;                  //线程池是否被允许运作,初始化线程池对象时置0,线程池销毁时置为1
};

#endif
//我的线程池源文件

#include "e_pthread_pool.h"

//开放接口1
E_PThread_Pool::E_PThread_Pool(unsigned int max,unsigned int min,unsigned int wait)
{
    //配置基本参数
    count = 0;		//当前线程池为空
    waitcount = 0;  //没有等待线程
    mincount = min;	//核心线程数(出厂配置)
    maxcount = max;	//最大线程数(能承受的最高配置)
    waitsec = wait;	//线程保活时长(过了时长还没接到任务,那就裁掉)
    Stop = false;	//允许运作

    for (unsigned i = 0; i < mincount; i++)
    {
        createThread();	//跳转到这个函数的实现->->->->->
    }
}

E_PThread_Pool::~E_PThread_Pool()
{
    destroyThread();	//销毁线程池
}

void E_PThread_Pool::createThread()
{
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, taskThread, (void *)this);	
    //以执行taskThread()为目的创建线程,跳转到taskThread()函数的实现 ->->->->->
   
    if (ret < 0)
        perror("pthread create error");
    else
        count++;
}

// 工作线程
void * E_PThread_Pool::taskThread(void *arg)
{
    pthread_detach(pthread_self()); //设置线程自分离属性
    E_PThread_Pool *pool=(E_PThread_Pool *)arg;
    while(1)
    {
//如果没有工作线程在等待
        if (pool->taskList.empty())
        {
            if(pool->Stop)	//当收到线程池停止运行的消息时
            {
                pool->count--;	//线程数减一
                pthread_exit(NULL); //本线程强制退出
            }

            pool->waitcount++;	//等待任务的线程数加一
            bool bSignal = pool->cond.timewait(pool->waitsec); //新任务等待被唤醒
            pool->waitcount--;	//没等到,没事干,喝西北风了

            // 删除无用线程
            if (!bSignal && pool->count > pool->mincount)	//如果没事干 && 有多余线程
            {
                pool->count--;	//先裁员一个,不要一次做绝了,反正是在while循环里面,没事干裁员机会多得是
                pthread_exit(NULL);
            }
        }
       
//如果有工作线程在等待
        if (!pool->taskList.empty())
        {
            pool->taskCond.lock();	//上任务锁
            Task *t = pool->taskList.front(); 	//获取任务队列中最前端的任务并执行
            pool->taskList.pop_front(); //移除被领取的任务
            pool->taskCond.unlock();//记得解锁

            t->run(); //任务开始
            delete t; //弄完就删了
        }
    }
    pthread_exit(NULL);
}

//开放接口2,向任务队列中添加任务
void E_PThread_Pool::addTask(Task *task)
{
    if (Stop)	//线程池是否停止工作
        return;

    //向任务队列中添加新任务
    taskCond.lock();	//上任务锁
        taskList.push_back(task);	//添加任务
    taskCond.unlock();	//记得解锁

    cond.lock();	//上线程锁
    if(waitcount)	//如果有空闲线程
    {
        cond.signal();	//唤醒一个线程
    }
    else if(count < maxcount)	//如果没有空闲线程,一般来说,走到这里面来,那这个线程池的设计是有点失败了	
    {
        createThread();	//那就创建一个
        cond.signal();	//然后唤醒
    }
    cond.unlock();
}


void E_PThread_Pool::destroyThread()
{
    printf("destroy?\n");

#if 0   //强行清理
    list_task::iterator it = taskList.begin();
    for (; it! = taskList.end(); it++)
    {
        Task *t = *it;
        delete t;

        t = NULL;
    }
    taskList.clear();
#endif

// 等待所有线程执行完毕
    Stop = true;
    while (count > 0)
    {
        cond.lock();
        cond.broadcast();	//广播
        cond.unlock();
        
        sleep(1);
    }
}

与[转帖]线程池实现与讲解相似的内容:

[转帖]线程池实现与讲解

https://cloud.tencent.com/developer/article/1992928?areaSource=&traceId= 文章目录 什么是线程池?为什么要用线程池? 示例代码与分析 什么是线程池?为什么要用线程池? 线程池,好东西啊,它有一池子的线程,所以叫线程池。 为什么说

[转帖]jmeter线程组与循环次数的区别

在压测的时候,有些接口需要携带登录信息,但是我们只想登录一次,然后其他接口进行多用户压测,此时你会怎么办?用仅一次控制器实现吗?下面我们来看看用仅一次控制器能不能实现 压测时jmeter中的线程数是模拟并发用户的,我们设置线程数5,然后登录请求添加一个仅一次控制器,我们通过察看结果树看到登录请求是执

[转帖]分享自己做的一个指定进程以及线程长时间cpu监控的工具

https://cdn.modb.pro/db/329601 前言: 前面给大家分享过一个工作中用到的编译拷贝脚本,其实工作中还有一些其他工具的使用,今天再来分享一个自己纯手工的CPU监控的脚本。大家可以结合上篇文章与本篇文章一起学习shell。 主要实现功能: 1.监控指定进程是否运行 2.读取该

[转帖]详解MySQL数据库原生的数据复制方式:异步复制、半同步复制与全同步复制

一、MYSQL复制架构衍生史 在2000年,MySQL 3.23.15版本引入了Replication。Replication作为一种准实时同步方式,得到广泛应用。这个时候的Replicaton的实现涉及到两个线程,一个在Master,一个在Slave。Slave的I/O和SQL功能是作为一个线程,

[转帖]新一代垃圾回收器ZGC的探索与实践

1. 引入 1.1 GC之痛 很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰。GC停顿指垃圾回收期间STW(Stop The World),当STW时,所有应用线程停止活动,等待GC停顿结束。以美团风控服务为例,部分上游业务要求风控服务65ms内返回结果,并且可用性要达到99.99%。

[转帖]《Linux性能优化实战》笔记(23)—— 内核线程 CPU 利用率过高,perf 与 火焰图

在排查网络问题时,我们还经常碰到的一个问题,就是内核线程的 CPU 使用率很高。比如,在高并发的场景中,内核线程 ksoftirqd 的 CPU 使用率通常就会比较高。回顾一下前面学过的 CPU 和网络模块,你应该知道,这是网络收发的软中断导致的。 要分析 ksoftirqd 这类 CPU 使用率比

[转帖]JVM(3)之垃圾回收(GC垃圾收集器+垃圾回收算法+安全点+记忆集与卡表+并发可达性分析......)

《深入理解java虚拟机》+宋红康老师+阳哥大厂面试题2总结整理 一、堆的结构组成 堆位于运行时数据区中是线程共享的。一个进程对应一个jvm实例。一个jvm实例对应一个运行时数据区。一个运行时数据区有一个堆空间。 java堆区在jvm启动的时候就被创建了,其空间大小也就被确定了(堆是jvm管理的最大

[转帖]REDIS SLOWLOG(慢日志)

slowlog 此命令用于读取和重置Redis慢查询日志。 一、Redis slowlog 官方概述: Redis Slow Log是一个用于记录超过指定执行时间的查询的系统。执行时间不包括与客户端交谈,发送答复等I / O操作,而仅包括实际执行命令所需的时间(这是命令执行的唯一阶段,在该阶段线程被

[转帖]进程 线程和 协程

作者:ScratchLab链接:https://www.zhihu.com/question/308641794/answer/2867920715来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 线程有两种实现方式:内核态线程和用户态线程。早期,内核态线程由于概念清晰

[转帖]jmeter实现不写代码把测试结果存入execl

这里使用数据库作为中间件来实现不写代码就把测试结果存入execl,下面是步骤 1.新建一个setup线程组用来设置数据库连接信息和新建数据库,如下图所示,我们使用sqlite数据库来存储信息,因为不需要自己再去安装数据库,Database URL填写jdbc:sqlite:mytest.db,这是标