线程池设计, 从简单的我们平常设计线程池图解,到生活中的类似线程池的处理现实场景, 到简单的C++模拟nginx写的单链表组织工作队列的简单线程池实现 + nginx 部分源码刨析

简介: 线程池设计, 从简单的我们平常设计线程池图解,到生活中的类似线程池的处理现实场景, 到简单的C++模拟nginx写的单链表组织工作队列的简单线程池实现 + nginx 部分源码刨析

活实例 整体 抽象 线程池, 其实线程池, 给我的感觉 核心 其实是 在于任务队列的设计上, 任务队列 + 互斥锁 + 条件变量 保证 任务队列的 中任务的有条不紊的 生产任务和 处理任务.........


这个池子: 其实 就是提前开启了 多个 死循环的处理任务的工作线程: 这些多个线程好比是现实生活中的办事窗口,任务队列就好比是我们去办事的人.....


看一张图: 人们排着队去 银行办事, 或者 医院挂号 办事:


其实这些  窗口就是我们的工作线程: 他们其实是一开始就知道自己的办事流程的,,,,   一直死循环的等待工作,,,,,     是提前 开启的,,,   是 时刻准备着的,  所以 是任务到来之前, 一创建线程池, 初始化线程池的时候, 就必须马上的启动这些工作线程......       (InitTreadPool就启动消费者线程)  

工作线程: 一般是 一直的 死循环的进行 等待 着 任务的到来 随时准备着 处理任务的........


任务队列:  生产者, 不停的 生产存储着需要解决的任务......    (存在 异步解耦合性质?????).....


下面 先理解好啥叫作异步解耦?????    


队列。。。  其实永远都是一个作用, 缓冲, 解除耦合性,,,,    然后一提到 解耦合 永远都有一个叫做   异步解耦的说法, 但是  这个概念总是非常的模糊的过去了。。。。。。。


异步解耦合: 就是两个事件之间是相互解除等待关系的,,,,,  (这个是我的简单的理解,如果有不对,欢迎各位给出改正, 我会非常表示感谢的,,,,,)


有了任务队列:  其实 在所有线程都在繁忙的时候, 我们提供任务的线程需要不需要等待 线程空闲下来,我们把任务给到线程手上,我们才返回,,,,去收集其他任务提交信息呢????


答案当然是不需要,  我们只是需要将任务给到任务队列中我们就不需要管了, 就可以直接返回了......  这样我们就不受工作线程繁忙的影响了, 提供任务的线程将任务放到任务队列中,然后就可以去继续获取其他任务信息放入到任务队列中.......  


还可以用生活中的 快递驿站的例子理解


比如说常见的菜鸟驿站, 快递员们将 快递包裹(任务) 放入到菜鸟驿站就OK了......   然后快递员就可以离开去干其他事情了......


同样我们人去拿取快递, 也不需要和快递员直接交互, 我们也不是说快递员将快递放入到了菜鸟驿站,我们就需要立马去拿, 不需要, 我们可以先将手头上正在处理的事情处理好,然后 抽空去拿就OK了.....


如上就是我对于异步解耦的理解, 就是 生产者来说 我 不需要等着你可以消费了,我才生产, 我生产好了可以屯在队列中, 消费者 不是说你生产了我立马必须消费, 可以等到我自己可以消费了再消费,,, 异步性我感觉有一定的自由性....


线程池说了这么多理论, 相信大家都快烦了, 如果大家是学生的话就像快点来撸代码了,,,, 我学这里的时候完全是一个想法, 哈哈哈, 奉上大致的两份代码如下:    C++ 版本


首先写代码的时候  需要了解一个点:    线程处理函数的第一个参数需要是  void* 类型


void *(*start_routine) (void *)   所以  这个  Routine 线程执行的函数我们必须要 在类中进行一个static  不然 第一个参数默认传入的是 this 指针, 这样会报错, 所以我们处理成static 函数,然后手动传入this指针实现


如下 首先是一个  thread_pool 的 代码:[tangyujie@VM-4-9-centos PthreadPool]$ cat pthread_pool.hpp

#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
using std::endl;
using std::cin;
using std::cout;
using std::queue;
template<class T>
class PthreadPool {
public:
    PthreadPool(int num = 3) :_num(num) {
        pthread_mutex_init(&_lock, NULL);
        pthread_cond_init(&_cond, NULL);
    }
    ~PthreadPool() {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
    void Lock() {
        pthread_mutex_lock(&_lock);             //上锁
    }
    void Unlock() {
        pthread_mutex_unlock(&_lock);           //解锁
    }
    void WakeUp() {                             //唤醒进程, 通知消费者线程有任务了
        pthread_cond_signal(&_cond);            //唤醒单个进程
    }
  void Wait() {                                 //没有任务, 消费者线程先阻塞起来等待任务 
        pthread_cond_wait(&_cond, &_lock);        
    }
    bool IsEmptyQueue() {
        return _taskqueue.empty();
    }
    static void* Routine(void* args) {          
        PthreadPool* self = (PthreadPool*)args;   
        while (1) {                      
            self->Lock();                           
            while (self->IsEmptyQueue()) {       //while  防止伪唤醒,
                //wait
                self->Wait();
            }
            //说明存在 task了/
            T t;
            self->PopTask(t);
            self->Unlock();                         
            t.Run();                            //解锁后处理任务.... 做啥???
        }
    } 
    void PushTask(const T& in) {                //传入参数, push task
        Lock();
        _taskqueue.push(in);                    //操作临界资源加锁
        Unlock();
        WakeUp();                                       
    }
    void PopTask(T& out) {                       //传出参数 拿取任务
        out = _taskqueue.front();                //拿取pop任务
        _taskqueue.pop();                        //任务队列pop 任务
    }
    void InitPthreadPool() {                       
        pthread_t tid;        
        for (int i = 0; i < _num; ++i) {
            pthread_create(&tid, NULL, Routine, this);
            pthread_detach(tid);                 //回收线程资源
        }
    }
private:
    int _num;                                   //工作线程的数目
    queue<T> _taskqueue;                        //任务队列
    pthread_mutex_t _lock;                      //锁保证临界资源的互斥访问
    pthread_cond_t _cond;                       //条件变量控制资源到来时候的唤醒工作
};

然后是  task.hpp的代码 生产任务:#[tangyujie@VM-4-9-centos PthreadPool]$ cat task.hpp

#pragma once
#include <iostream>
#include <pthread.h>
using std::endl;
using std::cout;
using std::cerr;
//typedef int (*handler_t ) (int, int, char);
class Task {
public:
    Task(int x = 1, int y = 1, char op = '+')                //默认构造 
        : _x(x)
        , _y(y)
        , _op(op)  
    {}
    ~Task(){}
    void Run() {
        int z = 0;
        switch(_op) {
            case '+':
                z = _x + _y;
                break;
            case '-':
                z = _x - _y;
                break;
            case '*':
                z = _x * _y;
                break;
            case '/':
                if (_y == 0) {cerr << "div zero!" << endl; break;}
                z = _x / _y;
                break;
            case '%':
                if (_y == 0) {cerr << "mod zero!" << endl; break;}
                z = _x % _y;
                break;
            default:
                cerr << "operator error!" << endl;
                break;
        } 
        cout << "thread: [" << pthread_self() << "]: "<< _x << _op << _y << "=" << z << endl; 
    }
private:
    int _x;
    int _y;
    char _op;
};

最后一个是 测试函数  main.cc的代码:[tangyujie@VM-4-9-centos PthreadPool]$ cat main.cc

#include "task.hpp"
#include "pthread_pool.hpp"
#include <cstdlib>
#include <ctime>
int main() {
    PthreadPool<Task> * tp = new PthreadPool<Task>();
    tp->InitPthreadPool();
    srand((unsigned long)time(nullptr));
    const char* ops = "+-*/%";
    while (1) {
        int x = rand() % 123 + 1;
        int y = rand() % 123 + 1;
        Task t(x, y, ops[rand() % 5]);
        tp->PushTask(t);
    sleep(1);
    }
    return 0;
}

至此: 基本对于线程池基本上是知道理论了:


然后如下 介绍一下  线程池  为啥好??????池化技术有啥好处????


首先:  线程池, 池子,  对于池子, 我的印象就是蓄水池了, 比如说在缺水的底带, 家里要大水如果需要到很远的地方去打水的化, 还有需要水的时候才接水.... 可以吗???  当然可以, 但是这样需要的时候立马打水  或者到远处去运输水  对于这个时间的消耗是不是很大, 要是 我们提前造一个池子, 需要的话直接去池子里面去拿取, 是不是效率会高很多.............


内存池, 线程池  其实都是常见的池化技术,   线程池, 避免了临时创建大量线程, 充分的实现了线程的复用, 我们可以反复利用我们最初线程池初始创建的时候就创建好的多线程     (工作线程)

自此:  算是暂时小结了, 写了太多了,,,, 其中都是鄙人的拙见, 如果觉得有帮助希望点个赞,欢迎评论, 最后 好了啦, 不卷了, 大家新年快乐, 学完收工.... 至于  nginx 部分源码刨析留着下次写吧

相关文章
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
564 5
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
1260 7
|
消息中间件 存储 安全
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
690 5
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
1028 6
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
508 0
C++ 多线程之线程管理函数
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
619 83
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
426 6
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
524 0
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
727 16