Qt實現簡單線程池

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

(附上程序源碼,歡迎大家指點錯誤)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章