以前一直使用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());
}
可以看到不 同的线程在处理队列中的任务。