使用c++11创建自己的线程池

    以前一直使用Qt的线程池,在Qt应用中还是很方便,Qt自带的线程池,每一个任务QRunable的run函数执行完后,线程会退出,有些时候是不需要退出的,这里可以在run函数中做一些操作;这里我用c++11写了一个自己的线程池。

     主要是用一个线程安全的队列、std::thread、容器实现的。首先我实现的队列如下。 

#ifndef CONCURRENTQUEUE_H
#define CONCURRENTQUEUE_H

#include <queue>
#include <mutex>
#include <condition_variable>
template<class Type>
class ConcurrentQueue
{
public:
    ConcurrentQueue():_queue(),_mutex(),_condition(){}
    ConcurrentQueue(const ConcurrentQueue &) = delete; //不允许赋值构造
    ConcurrentQueue &operator = (const ConcurrentQueue &) = delete; //不允许拷贝构造
    virtual ~ConcurrentQueue(){}

    void Push(Type record){
        std::lock_guard<std::mutex> lock(_mutex);
        _queue.push(record);  //加入队列
        _condition.notify_one();
    }

    bool Pop(Type &record, bool isBlocked = true){ //根据参数决定是否阻塞的获取任务
        if(isBlocked){
            std::unique_lock<std::mutex> lock(_mutex);
            _condition.wait(lock,[this]{return !_queue.empty();});
        }else{
            std::lock_guard<std::mutex> lock(_mutex);
            if(_queue.empty()){
                return false;
            }
        }

        record = std::move(_queue.front()); //move生成一个右引用,使用移动构造将值赋给外面
        _queue.pop();
        return true;
    }

    int32_t Size() {
        std::lock_guard<std::mutex> lock(_mutex);
        return _queue.size();
    }

    bool Empty(){
        std::lock_guard<std::mutex> lock(_mutex);
        return _queue.empty();
    }

private:
    std::queue<Type> _queue;
    mutable std::mutex _mutex;
    std::condition_variable _condition;
};

      在队列的实现中使用了c++11的条件变量和锁来解决互斥问题,在Pop函数中通过参数可以控制是否以阻塞方式获取任务, 

获取到任务时,使用移动构造传递出去;另外定义的std::mutex加了一个mutable限定各个符,这样在类成员的const函数中也可

以访问和修改该成员变量了。

     其次我实现了线程池,由于是模板,我将定义放在了tcc文件中。声明和定义如下 

#ifndef THREADPOOL_H
#define THREADPOOL_H

#define MIN_THREADS 10
#include "concurrentqueue.h"
#include <thread>
#include <functional>

template<class Type>
class ThreadPool{
public:
    ThreadPool(const ThreadPool&) = delete; //不允许赋值构造
    ThreadPool &operator = (const ThreadPool&) = delete; //不允许拷贝构造
    ThreadPool(int32_t threads,std::function<void(Type &record)>);
    virtual ~ThreadPool();
    void Submit(Type record);

private:
    bool _shutdown;
    int32_t _threads;
    std::function<void(Type &record)> _handler;
    std::vector<std::thread> _workers;
    ConcurrentQueue<Type> _tasks;
};

#include "thread_pool.tcc"
#endif // THREADPOOL_H

      和前面一个去掉了赋值运算符和拷贝构造,在声明结尾包含了tcc的实现文件 

#pragma once

template<class Type>
ThreadPool<Type>::ThreadPool(int32_t threads, std::function<void(Type &record)> handler):
    _shutdown(false),
    _threads(threads),
    _handler(handler),
    _workers(),
    _tasks(){
    if(_threads < MIN_THREADS)
        _threads = MIN_THREADS;

    for(int32_t i = 0; i < _threads; i++){
        _workers.emplace_back( //通过一个匿名函数直接隐式调用std::thread的构造函数
            [this]{
                while (!_shutdown) { //控制退出
                    Type record;
                    _tasks.Pop(record,true); //阻塞获取
                    _handler(record);  //处理任务
                }
            }
        );
    }
}

template<class Type>
ThreadPool<Type>::~ThreadPool(){
    for(std::thread &worker : _workers){ //退出时等待每一个线程退出
        worker.join();
    }
}

template<class Type>
void ThreadPool<Type>::Submit(Type record){
    _tasks.Push(record);   //将任务添加到任务队列
}

       通过传入的线程数量开启线程,并在每个线程中 运行处理函数,这里的emplace_back是c++11新增的添加元素方法,效率更高,不会触发移动构造和拷贝构造。在Submit函数中将任务添加到队列,在处理函数中循环的从队列中取任务并进行处理。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "EpollServer/threadpool.h"
QT_FORWARD_DECLARE_CLASS(QTextEdit)
QT_FORWARD_DECLARE_CLASS(QPushButton)
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

signals:
    void sigMsg(QString);

private:
    QTextEdit *m_textEdit{nullptr};
    QPushButton *m_pushBtn{nullptr};
    ThreadPool<QString> *m_thrPool{nullptr};

private slots:
    void slotPushBtnClicked();
};

#endif // WIDGET_H

    在linux下的QtCreator中建了一个界面,主要用于测试。 

#include "widget.h"
#include <QTextEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include "serverthread.h"
#include <QDebug>

void handlerFunc(QString &msg){
    qDebug() << QThread::currentThreadId() << msg;
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout *mainLay = new QVBoxLayout;
    m_textEdit = new QTextEdit;
    m_pushBtn = new QPushButton("push");
    QHBoxLayout *hlay = new QHBoxLayout;
    hlay->addStretch();
    hlay->addWidget(m_pushBtn);
    mainLay->addWidget(m_textEdit);
    mainLay->addLayout(hlay);
    setLayout(mainLay);

    connect(m_pushBtn,SIGNAL(clicked(bool)),this,SLOT(slotPushBtnClicked()));
    m_thrPool = new ThreadPool<QString>(3,::handlerFunc);
}

Widget::~Widget()
{

}

void Widget::slotPushBtnClicked()
{
    m_thrPool->Submit(m_textEdit->toPlainText());
}

   可以看到不 同的线程在处理队列中的任务。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值