《Linux C++》線程池

1、爲什麼使用線程池

線程池的出現正是着眼於減少線程本身帶來的開銷,避免“即時創建,即時銷燬”。

2、線程池應用場合

像大多數網絡服務器,包括Web服務器、Email服務器以及數據庫服務器處理數目巨大的連接請求,但處理時間卻相對較短,並且實時性要求比較高的情況。

3、實現流程

(1)設置生產者/消費者模式,臨界資源;

說明:這裏的生產者是任務隊列,消費者是線程隊列,臨界資源相當於一個個的任務;

(2)創建n個線程,加鎖,去任務隊列取任務,pop任務,解鎖

說明:如果任務隊列沒有任務,則線程阻塞,等待條件變量解阻塞;

(3)有新的任務來了之後,加鎖,push到任務隊列,解鎖,通過條件變量喚醒阻塞的線程

4、Linux C++實現

參考github:https://github.com/progschj/ThreadPool

(1)頭文件ThreadPool.h

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

class ThreadPool {

public:
    ThreadPool(size_t);                          //構造函數
    template<class F, class... Args>             //類模板
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;//任務入隊
    ~ThreadPool();                              //析構函數

private:
    std::vector< std::thread > workers;            //線程隊列,每個元素爲一個Thread對象
    std::queue< std::function<void()> > tasks;     //任務隊列,每個元素爲一個函數對象
    std::mutex queue_mutex;                        //互斥量
    std::condition_variable condition;             //條件變量
    bool stop;                                     //停止
};

// 構造函數,把線程插入線程隊列,插入時調用embrace_back(),用匿名函數lambda初始化Thread對象
inline ThreadPool::ThreadPool(size_t threads) : stop(false){

    for(size_t i = 0; i<threads; ++i)
        workers.emplace_back(
            [this]
            {
                for(;;)
                {
                    // task是一個函數類型,從任務隊列接收任務
                    std::function<void()> task;
                    {
                        //給互斥量加鎖,鎖對象生命週期結束後自動解鎖
                        std::unique_lock<std::mutex> lock(this->queue_mutex);

                        //(1)當匿名函數返回false時才阻塞線程,阻塞時自動釋放鎖。
                        //(2)當匿名函數返回true且受到通知時解阻塞,然後加鎖。
                        this->condition.wait(lock,[this]{ return this->stop || !this->tasks.empty(); });

                         if(this->stop && this->tasks.empty())
                            return;

                        //從任務隊列取出一個任務
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }                            // 自動解鎖
                    task();                      // 執行這個任務
                }
            }
        );
}

// 添加新的任務到任務隊列
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
    -> std::future<typename std::result_of<F(Args...)>::type>
{
    // 獲取函數返回值類型
    using return_type = typename std::result_of<F(Args...)>::type;

    // 創建一個指向任務的智能指針
    auto task = std::make_shared< std::packaged_task<return_type()> >(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );

    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);  //加鎖
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
        tasks.emplace([task](){ (*task)(); });          //把任務加入隊列
    }                                                   //自動解鎖
    condition.notify_one();                             //通知條件變量,喚醒一個線程
    return res;
}

// 析構函數,刪除所有線程
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::thread &worker: workers)
        worker.join();
}

#endif

(2)main.cpp

#include <iostream>
#include <unistd.h>
#include "ThreadPool.h"
using namespace std;
void task1(){
    while(1){
        cout<<"task1 thread ID:"<<this_thread::get_id()<<endl;
        sleep(1);
    }
}

void task2(){
    while(1){
        cout<<"task2 thread ID:"<<this_thread::get_id()<<endl;
        sleep(2);
    }
}

int main()
{
    ThreadPool pool(4);
    pool.enqueue(task1);
    pool.enqueue(task2);
}

 

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