linux线程池模拟实现

今天模拟实现了个线程池,怎么说,感觉整体还是比较容易的,线程池和内存池,进程池等等,大概就是一个意思,例如内存池,就是提前申请好内存,然后等你用的时候再去其中拿就可以了,线程池是一个道理,就是现申请好一个,等我们有任务时,去让线程取任务。代码如下:

#pragma once
#include 
#include 
#include 
#include 
#include 
#include "thread.hpp"
#include "myMtx.hpp"
#include "cond.hpp"
using namespace std;
#define NUMPTHREAD 5
#define NUMTAK 5
typedef void *(*func)(void *);
//饿汉单列模型
template 
class pthreadPool
{
public:
    static pthreadPool *get()
    {
        return _mypool;
    }

private:
    pthreadPool(int nump = NUMPTHREAD, int numt = NUMTAK) : _numpthread_t(nump), _tasknum(numt)
    {
        pthread_cond_init(&_full, nullptr);
        pthread_cond_init(&_empty, nullptr);
        pthread_mutex_init(&_mtx, nullptr);
    }

public:
    void start()
    {
        _func = pthreadPool::enter;
        for (int i = 0; i < _numpthread_t; i++)
        {
            thread *p = new thread(_func, this);
            _pthread_t.push_back(p);
        }
    }
    void pop(T &x)
    {
        myMtx mtx(_mtx);
        myCond me(_empty, _mtx);
        myCond mf(_full, _mtx);
        while (_task.size() == 0)
        {
            mf.singal();
            me.wait();
        }
        sleep(5);
        cout << "消费" << pthread_self() << endl;
        x = _task.front();
        _task.pop();
    }
    static void *enter(void *args)
    {
        pthreadPool *td = reinterpret_cast *>(args);
        while (true)
        {
            T ret;
            td->pop(ret);
            cout << ret << endl;
        }
        return nullptr;
    }
    // 插入任务
    void task(const T &x)
    {
        myMtx m(_mtx);
        myCond me(_empty, _mtx);
        myCond mf(_full, _mtx);
        while (_task.size() == _tasknum)
        {
            me.singal();
            mf.wait();
        }
        cout << "生产" << pthread_self() << endl;
        sleep(5);
        _task.push(x);
    }
    ~pthreadPool()
    {
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
        pthread_mutex_destroy(&_mtx);
        for (auto &e : _pthread_t)
            delete e;
    }

private:
    vector _pthread_t;
    queue _task;
    func _func;
    int _numpthread_t;
    int _tasknum;
    pthread_mutex_t _mtx;
    pthread_cond_t _full;
    pthread_cond_t _empty;
    static pthreadPool *_mypool;
};
template 
class pthreadPool;
template 
pthreadPool *pthreadPool::_mypool = new pthreadPool();
#pragma once
#include 
#include 
class myCond
{
public:
    myCond(pthread_cond_t &cond, pthread_mutex_t &mtx) : _mcond(cond), _mtx(mtx)
    {
    }
    void wait()
    {
        pthread_cond_wait(&_mcond,&_mtx);
    }
    void singal()
    {
        pthread_cond_signal(&_mcond);
    }
private:
    pthread_cond_t &_mcond;
    pthread_mutex_t &_mtx;
};
#pragma once
#include 
#include 
class myMtx
{
public:
    myMtx(pthread_mutex_t &mtx) : _mtx(mtx)
    {
        pthread_mutex_lock(&_mtx);
        std::cout << "加锁" << std::endl;
    }
    ~myMtx()
    {
        pthread_mutex_unlock(&_mtx);
        std::cout << "解锁" << std::endl;
    }

private:
    pthread_mutex_t &_mtx;
};
#pragma once
#include 
#include 
class myMtx
{
public:
    myMtx(pthread_mutex_t &mtx) : _mtx(mtx)
    {
        pthread_mutex_lock(&_mtx);
        std::cout << "加锁" << std::endl;
    }
    ~myMtx()
    {
        pthread_mutex_unlock(&_mtx);
        std::cout << "解锁" << std::endl;
    }

private:
    pthread_mutex_t &_mtx;
};
#include "pthreadPool.hpp"
#include 

int main()
{
    srand((size_t)time(nullptr) ^ 1100 + 1);
    pthreadPool *p = pthreadPool::get();
    p->start();
    while (true)
    {
        p->task(rand() % 100 + 1);
        cout << "插入成功" << endl;
    }
    delete p;
    return 0;
}

总体实现如上,逻辑还是比较像生产消费模型的,总体进行了锁的封装,没有写打印日志的函数。

实现的时候,个人认为可以放任务的队列实现成static,如果实现成static的话,那么此时的所有对象只有一个任务队列,但是话又说回来,如果创建多个线程池的对象,那么就只有一个任务队列,貌似也不符合逻辑,有这种想法的其实错了,因为线程池就像内存池一样,只有一个,所以我最后把其设计成了单列模式,用的是饿汉模式,因为饿汉模式没有线程安全问题,所以我用了饿汉模式实现你的单利。这只是个人想法,也可以不用把任务队列实现为static。

然后锁和条件变量都是用RAII的风格来实现的,这样看代码比较简洁。

实现的注意事项,个人认为还是线程访问任务队列的哪里比较绕,因为我的每一个线程是封装了的,所以他创建线程是调用的函数必须是静态的,但是如果这个函数是静态的话,那么此时他就不可以访问其他的成员变量了,所以此时必须要传this指针才可以,有些伙伴可能想的是不封装线程,不封装线程其实是一样的,如果你把创建线程的函数实现在类内,那么就不可以调用,也必须是静态成员函数才可以,如果实现在类外,因为函数中要访问其内部的成员,所以此时必须是友元函数才可以,但是此时问题又来了,因为我们线程池的实现是模版,所以你必须把这个函数也实现为模版类型,所以,这里一定要注意。

以上就是我实现的过程,想看源码的朋友可以点这里https://gitee.com/stickykkkkkk/linux/tree/master/dm11-13

最后希望大家能够支持,谢谢!!!

你可能感兴趣的:(Linux相关知识,linux,运维,服务器)