Qt本身就具備線程池類QThreadPool,使用起來也很方便,不過現在我們用QThread來做一個自己的線程池。
創建用來管理線程池的類ThreadPool,在這裏它發揮着管家的職責,掌管公共資源的使用。由於管理任務的隊列、鎖以及條件變量都聲明瞭私有,所以給WorkThread聲明爲友元類,以便其能訪問。
爲了簡單,我們只定義了一個構造函數以及三個函數:
-
ThreadPool(int count,QObject*parent=nullptr) 其中count表示要在線程池中創建多少個子線程
- void PushTask(Task) 用於添加待執行的任務,Task是個函數對象,我們在調用的時候就不用顯示的指定參數了,其功能就是實現簡單的加法
- void Start() 控制開始運行
- void Destroy() 銷燬所有子線程
關於子線程的管理我們使用的智能指針,以方便資源自動釋放。下面就是ThreadPool的實現:
//頭文件
//線程池
class ThreadPool : public QObject
{
Q_OBJECT
public:
explicit ThreadPool(int count,QObject *parent = nullptr);
~ThreadPool();
void PushTask(Task);
void Start();
void Destroy();
private:
QList<Task> m_Tasks; // 任務隊列
QList<QSharedPointer<WorkThread>> m_Threads; //運行的線程
bool m_bStop; //停止線程
QMutex m_TaskMutex;
QWaitCondition m_NotEmpty;
friend class WorkThread;
};
//實現
ThreadPool::ThreadPool(int count,QObject *parent) : QObject(parent),m_bStop(false)
{
for(int i=0;i<count;i++)
m_Threads.push_back( QSharedPointer<WorkThread>(new WorkThread(this)));
}
ThreadPool::~ThreadPool()
{
Destroy();
}
void ThreadPool::PushTask(Task task)
{
m_TaskMutex.lock();
m_Tasks.push_back(task);
m_NotEmpty.wakeOne();
m_TaskMutex.unlock();
}
void ThreadPool::Start()
{
foreach (auto thread, m_Threads)
{
thread->start();
}
}
void ThreadPool::Destroy()
{
m_bStop = true;
m_NotEmpty.wakeAll();
//等待線程退出
foreach (auto thread, m_Threads)
{
if(!thread->isFinished())
thread->wait();
}
//清空 智能指針自動釋放
m_Threads.clear();
}
以WorkThread做爲線程池中的子線程,所以要繼承QThread,並重新實現run()函數。
由於公共資源都在ThreadPool中,所以我們定義了一個父類指針m_Pool來使用公共資源。
還定義了 m_Handle,主要爲了下析構時打印線程ID,關於這塊大家可以參考我以前測試的一個例子https://blog.csdn.net/hanzhaoqiao1436/article/details/80957945
實現如下:
//頭文件
//執行線程
class WorkThread:public QThread
{
public:
WorkThread(ThreadPool* parent);
~WorkThread();
protected:
virtual void run();
private:
ThreadPool* m_Pool;
Qt::HANDLE m_Handle;
};
//實現
WorkThread::WorkThread(ThreadPool *parent)
{
m_Pool = parent;
}
WorkThread::~WorkThread()
{
qDebug() << __FUNCTION__ << m_Handle << "Thread quit: " << isFinished();
}
void WorkThread::run()
{
m_Handle = QThread::currentThreadId();
while(!m_Pool->m_bStop)
{
m_Pool->m_TaskMutex.lock();
while(m_Pool->m_Tasks.isEmpty())
{
qDebug() << QThread::currentThreadId() << QString::fromLocal8Bit("進入等待");
m_Pool->m_NotEmpty.wait(&m_Pool->m_TaskMutex);
qDebug() << QThread::currentThreadId() << QString::fromLocal8Bit("被喚醒");
//線程退出條件判斷
if(m_Pool->m_bStop)
{
m_Pool->m_TaskMutex.unlock();
return;
}
}
//取出任務
Task task = m_Pool->m_Tasks.front();
m_Pool->m_Tasks.pop_front();
m_Pool->m_TaskMutex.unlock();
if(task)
{
//執行任務
task();
}
}
}
差點忘了貼函數對象Task的定義-_-|| I'm sorry
//任務類型
class Task
{
public:
Task(int a,int b):m_a(a),m_b(b){}
void operator ()()const
{
QThread::msleep(300);
qDebug() << QThread::currentThreadId() << QString::fromLocal8Bit("執行任務") << m_a << "+" << m_b << "=" << m_a+m_b;
}
operator bool() const
{
//如果做除法這裏可以判斷下
if(m_b == 0)
return false;
return true;
}
private:
int m_a,m_b;
};
調用測試:
//創建三個子線程
m_Pool(new ThreadPool(3,this));
//直接啓動線程
m_Pool->Start();
//此時能看到所有線程都進入等待狀態
QThread::msleep(100);
//添加任務
for(int i=0;i<4;i++)
{
m_Pool->PushTask(Task(qrand(),qrand()));
}
運行結果:
0x17fc "進入等待"
0x1990 "進入等待"
0x1298 "進入等待"
0x1298 "被喚醒"
0x1990 "被喚醒"
0x17fc "被喚醒"
0x1990 "執行任務" 26500 + 6334 = 32834
0x1298 "執行任務" 18467 + 41 = 18508
0x17fc "執行任務" 15724 + 19169 = 34893
0x1298 "進入等待"
0x17fc "進入等待"
0x1990 "執行任務" 29358 + 11478 = 40836
0x1990 "進入等待"
(調用Destroy)
0x1298 "被喚醒"
0x17fc "被喚醒"
0x1990 "被喚醒"
~WorkThread 0x1298 Thread quit: true
~WorkThread 0x1990 Thread quit: true
~WorkThread 0x17fc Thread quit: true
假如我們在Destroy()中,不停止直接釋放子線程,線程會自動停下來嗎?答案當然是否定的。比如說我們註釋掉停止的相關調用
void ThreadPool::Destroy()
{
//m_bStop = true;
//m_NotEmpty.wakeAll();
//等待線程退出
//foreach (auto thread, m_Threads)
//{
// if(!thread->isFinished())
// thread->wait();
//}
//清空 智能指針自動釋放
m_Threads.clear();
}
(會看到如下結果)
~WorkThread 0x1958 Thread quit: false
QThread: Destroyed while thread is still running
(附上程序源碼,歡迎大家指點錯誤)