linux线程池

经过前面的阻塞队列和环形阻塞队列的学习。我们现在来实现一下多线程

/threadpool.h /

/* 线程池:

  • 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
  • 线程池的应用场景:
    1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
    1. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
    1. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,
      出现错误.
  • 线程池的种类:
  • 线程池示例:
    1. 创建固定数量线程池,循环从任务队列中获取任务对象,
    1. 获取到任务对象后,执行任务对象中的任务接口
      */

实现步骤1,我们先自定义一个线程类

这个类的解析:

1 我们先定义一个函数类型

申明一个返回值是void* 参数是void*的一个函数

cpp 复制代码
typedef std::function<void*(void*)> func_t;

2 构造函数

这个构造函数和之前写的构造函数不一样,这里构造函数只是为了初始化_name属性。

cpp 复制代码
    Thread()
    {
        char buffer[num];
        snprintf(buffer, sizeof buffer,"thread-%d",threadnum++);
        _name = buffer;
    }

3 start函数,我们把对线程的初始化放到了start函数里面并且通过传递this指针的方式,将本个类的属性传过去。

cpp 复制代码
    void start(func_t func,void *args=nullptr)
    {
        // Context* ctx = new Context();
        // ctx->_this=this;
        // ctx->_args =_args;
        _func =func;
        _args =args;
        int n = pthread_create(&_tid, nullptr, start_routine, this);
        assert(n == 0); //编译debug的方式发布的时候存在,release方式发布,assert就不存在了,n就是一个定义了,但是没有被使用的变量
        // 在有些编译器下会有warning
        (void)n;
    }

4 start_routine创建线程执行的函数

cpp 复制代码
    static void* start_routine(void*args)
    {
        Thread* _this = static_cast<Thread*>(args);
        // Context *ctx = static_cast<Context *>(args);
        // void *ret = ctx->_this->_func();
        // delete ctx;
        // return ret;
        return _this->callback();
    }

完成的代码

cpp 复制代码
#pragma once
#include<iostream>
#include<string>
#include<pthread.h>
#include <stdio.h>
#include<unistd.h>
#include <functional>
#include<cassert>

// 首先我们需要什么 , 名字 ,id ,需要的执行的函数, 还有线程属性可以包括线程的堆栈大小、调度策略、分离状态等。

// class Thread;

//上下文,当成一个大号的结构体
// class Context
// {
//     typedef std::function<void*(void*)> func_t;
// public:
//     Thread *_this;
//     void *_args;
//     func_t func;
// public:
//     Context():_this(nullptr), _args(nullptr),_func(nullptr)
//     {}
//     ~Context()
//     {}
// };


class Thread
{
private:
    static void* start_routine(void*args)
    {
        Thread* _this = static_cast<Thread*>(args);
        // Context *ctx = static_cast<Context *>(args);
        // void *ret = ctx->_this->_func();
        // delete ctx;
        // return ret;
        return _this->callback();
    }
public:
    // 这里就是申明了一种函数类型
    typedef std::function<void*(void*)> func_t;
    const int num = 1024;
public:
    Thread()
    {
        char buffer[num];
        snprintf(buffer, sizeof buffer,"thread-%d",threadnum++);
        _name = buffer;
    }

    void join()
    {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;
    }

    void start(func_t func,void *args=nullptr)
    {
        // Context* ctx = new Context();
        // ctx->_this=this;
        // ctx->_args =_args;
        _func =func;
        _args =args;
        int n = pthread_create(&_tid, nullptr, start_routine, this);
        assert(n == 0); //编译debug的方式发布的时候存在,release方式发布,assert就不存在了,n就是一个定义了,但是没有被使用的变量
        // 在有些编译器下会有warning
        (void)n;
    }

    std::string threadname()
    {
        return _name;
    }
     void *callback() 
    { 
        return _func(_args);
    }

    ~Thread()
    {
        // do nothing
    }
private:
    std::string _name; // 线程名字
    pthread_t _tid;   // 线程id
    void* _args; // 这个其实是pthread_create 函数的const pthread_attr_t attr属性 。             // 如果传入 NULL,线程将使用默认属性创建。线程属性可以包括线程的堆栈大小、调度策略、分离状态等。
    func_t _func; // 执行的任务函数
    static int threadnum;
};
 int Thread::threadnum = 1;

任务类Task.hpp,定义一种任务并且有一个函数对应

cpp 复制代码
#pragma once
#include <functional>
#include <stdio.h>
#include <string>
#include <iostream>

class Task
{
public:
    // 申明一种函数类型
    using func_t = std::function<int(int, int, char)>;

    // 构造方法
    Task() {}
    Task(int x, int y, char op, func_t func)
        : _x(x), _y(y), _op(op), _func(func)
    {
    }

    std::string operator()()
    {
        int result = _func(_x, _y, _op);
        char buffer[1024];
        snprintf(buffer, sizeof buffer, "%d %c %d=%d", _x, _op, _y, result);
        return buffer;
    }
    std::string toTaskString()
    {
        char buffer[1024];
        snprintf(buffer, sizeof buffer, "%d %c %d = ?", _x, _op, _y);
        return buffer;
    }

private:
    int _x;
    int _y;
    char _op;
    func_t _func;
};

int mymath(int x, int y, char op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = x + y;
        break;
    case '-':
        result = x - y;
        break;
    case '*':
        result = x * y;
        break;
    case '/':
        if (y == 0)
        {
            std::cout << "除0错误" << std::endl;
        }
        result = x / y;
        break;
    case '%':
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
            result = x % y;
            break;
    default:
        break;
    }
    return result;
}

线程池类。重点

cpp 复制代码
#include "Thread.hpp"
#include "Mutex.hpp"
#include <vector>
#include <queue>
#include <mutex>
#include <pthread.h>
const int gnum = 3;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadPool<T> *threadPool;
    std::string name;

public:
    ThreadData(ThreadPool<T> *tp, const std::string &n)
        : threadPool(tp), name(n)
    {
    }
};

template <class T>
class ThreadPool
{
private:
    static void *handlerTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            {
                LockGuard LockGuard(td->threadPool->getMutex());
                // std::cout << td->threadPool->isQueueEmpty() << std::endl;
                while (td->threadPool->isQueueEmpty())
                {
                    td->threadPool->threadWait();
                }
                t = td->threadPool->pop();
            }

            std::cout << td->name << " 获取了一个任务: " << t.toTaskString() << " 并处理完成,结果是:" << t() << std::endl;
        }
        delete td;
        return nullptr;
    }
    

public:
	ThreadPool(int num = gnum)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < _num; i++)
        {
            _thread.push_back(new Thread());
        }
    }
    void push(const T &t)
    {
        LockGuard lockguard(&_mutex);
        _queue.push(t);
        pthread_cond_signal(&_cond);
    }
    // 判断任务队列是否为空
    bool isQueueEmpty()
    {
        return _queue.empty();
    }
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    T pop()
    {
        T t = _queue.front();
        _queue.pop();
        return t;
    }

    void run()
    {
        for (const auto &t : _thread)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());
            t->start(handlerTask, td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }

    pthread_mutex_t *getMutex()
    {
        return &_mutex;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        // 验证一下这里的类型
        for (const auto &t : _thread)
        {
            delete t;
        }
    }


private:
    int _num;                      // 你创建线程的数量
    std::queue<T> _queue;          // 这里我查了,stl容器基于链表实现的 // 充当任务队列
    std::vector<Thread *> _thread; // 线程池的线程,用vector管理
    pthread_mutex_t _mutex;        // 定义一个锁对象
    pthread_cond_t _cond;          // 定义条件变量

};

构造函数

对成员属性初始化

cpp 复制代码
    ThreadPool(int num = gnum)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < _num; i++)
        {
            _thread.push_back(new Thread());
        }
    }

Run函数

这里回去调用线程类的start方法。

cpp 复制代码
    void run()
    {
        for (const auto &t : _thread)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());
            t->start(handlerTask, td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }

handlerTask方法

cpp 复制代码
static void *handlerTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            {
                LockGuard LockGuard(td->threadPool->getMutex());
                // std::cout << td->threadPool->isQueueEmpty() << std::endl;
                while (td->threadPool->isQueueEmpty())
                {
                    td->threadPool->threadWait();
                }
                t = td->threadPool->pop();
            }

            std::cout << td->name << " 获取了一个任务: " << t.toTaskString() << " 并处理完成,结果是:" << t() << std::endl;
        }
        delete td;
        return nullptr;
    }

test.cc

cpp 复制代码
#include"ThreadPool.hpp"
#include "Task.hpp"
#include <memory>
#include <unistd.h>

int main()
{
    ///一大堆的代码...

    // std::unique_ptr<ThreadPool<Task> > tp(new ThreadPool<Task>());
    ThreadPool<Task>::getInstance()->run();
    // tp->run();

    int x, y;
    char op;
    while(1)
    {
        std::cout << "请输入数据1# ";
        std::cin >> x;
        std::cout << "请输入数据2# ";
        std::cin >> y;
        std::cout << "请输入你要进行的运算#";
        std::cin >> op;
        Task t(x, y, op, mymath);
        // std::cout << "你刚刚录入了一个任务: " << t.toTaskString() << ", 确认提交吗?[y/n]# ";
        // char confirm;
        // std::cin >> confirm;
        // if(confirm == 'y') tp->push(t);
        ThreadPool<Task>::getInstance()->push(t);
        sleep(1);
    }
}

线程池其实是一个单例,将线程池的设计变为单例模式

cpp 复制代码
#include "Thread.hpp"
#include "Mutex.hpp"
#include <vector>
#include <queue>
#include <mutex>
#include <pthread.h>
const int gnum = 3;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadPool<T> *threadPool;
    std::string name;

public:
    ThreadData(ThreadPool<T> *tp, const std::string &n)
        : threadPool(tp), name(n)
    {
    }
};

template <class T>
class ThreadPool
{
private:
    static void *handlerTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            {
                LockGuard LockGuard(td->threadPool->getMutex());
                // std::cout << td->threadPool->isQueueEmpty() << std::endl;
                while (td->threadPool->isQueueEmpty())
                {
                    td->threadPool->threadWait();
                }
                t = td->threadPool->pop();
            }

            std::cout << td->name << " 获取了一个任务: " << t.toTaskString() << " 并处理完成,结果是:" << t() << std::endl;
        }
        delete td;
        return nullptr;
    }
    ThreadPool(int num = gnum)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < _num; i++)
        {
            _thread.push_back(new Thread());
        }
    }

public:
    void push(const T &t)
    {
        LockGuard lockguard(&_mutex);
        _queue.push(t);
        pthread_cond_signal(&_cond);
    }
    // 判断任务队列是否为空
    bool isQueueEmpty()
    {
        return _queue.empty();
    }
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    T pop()
    {
        T t = _queue.front();
        _queue.pop();
        return t;
    }

    void run()
    {
        for (const auto &t : _thread)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());
            t->start(handlerTask, td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }

    pthread_mutex_t *getMutex()
    {
        return &_mutex;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        // 验证一下这里的类型
        for (const auto &t : _thread)
        {
            delete t;
        }
    }
    static ThreadPool<T> *getInstance()
    {
        if (nullptr == tp)
        {
            _singlock.lock();
            if (nullptr == tp)
            {
                tp = new ThreadPool<T>();
            }
            _singlock.unlock();
        }
        return tp;
    }

private:
    int _num;                      // 你创建线程的数量
    std::queue<T> _queue;          // 这里我查了,stl容器基于链表实现的 // 充当任务队列
    std::vector<Thread *> _thread; // 线程池的线程,用vector管理
    pthread_mutex_t _mutex;        // 定义一个锁对象
    pthread_cond_t _cond;          // 定义条件变量
    static std::mutex _singlock;
    static ThreadPool<T> *tp;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::tp = nullptr;
template <class T>
std::mutex ThreadPool<T>::_singlock; //这是std里面的mutex ,跟linux里面的不一样
相关推荐
烦躁的大鼻嘎7 分钟前
【Linux】深入理解GCC/G++编译流程及库文件管理
linux·运维·服务器
乐大师7 分钟前
Deepin登录后提示“解锁登陆密钥环里的密码不匹配”
运维·服务器
ac.char13 分钟前
在 Ubuntu 上安装 Yarn 环境
linux·运维·服务器·ubuntu
敲上瘾14 分钟前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
长弓聊编程32 分钟前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.39 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
儿时可乖了1 小时前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
ruleslol1 小时前
java基础概念37:正则表达式2-爬虫
java