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);
}