線程池的概念
什麼是線程池
顧名思義,線程池就是一個有很多空閒線程的池子(線程的數量受到限制),需要用到多執行流進行任務處理時,就從池子中喚醒一個線程去處理任務
線程池的優點
- 避免大量線程頻繁創建和銷燬帶來的時間成本
:如果在一開始即創建好線程,要用的時候直接從線程池中取出,用完再放回,這樣就大大減少了創建與銷燬帶來的時間成本 - 避免無限制的線程創建導致資源耗盡
:線程池限制了線程的數量,這樣就保證了不會因爲線程創建過多導致資源耗竭,程序崩潰的情況
線程池的應用場景
有大量的數據處理請求,需要多執行流併發/並行處理
線程池的實現
實現思路
首先線程池的核心,就是大量的線程和一個任務緩衝隊列。在線程池創建時就提前創建好一定數量的線程,如果有任務到來則將任務放入緩衝隊列中,此時喚醒線程池中的線程取出任務進行處理,如果線程池沒有空閒線程,則阻塞任務,直到有線程處理完任務迴歸線程池中。同時,因爲不同任務有不同的處理方法,所以要開放接口給外界,處理的數據和方法由外界自己定,線程池只負責調用對應的任務處理方法進行處理,不關心其中的內容。這樣就更具有靈活性。
代碼實現
下面就來實現一個線程池,具體的細節都有註釋
#include<iostream>
#include<pthread.h>
#include<cstdlib>
#include<queue>
//類型重命名,將處理的方法定義爲一個函數指針
typedef void (*handler_t)(int);
const size_t MAX_SIZE = 10;
//任務
class ThreadTask
{
public:
//設置需要處理的數據與對應的處理方法
void SetTask(int data, handler_t handler)
{
_data = data;
_handler = handler;
}
//處理數據
void Run()
{
_handler(_data);
}
private:
int _data;
handler_t _handler;
};
//線程池
class ThreadPool
{
public:
ThreadPool(size_t capacity = MAX_SIZE) : _capacity(capacity)
{
pthread_cond_init(&_cond, NULL);
pthread_mutex_init(&_mutex, NULL);
//線程池提前創建線程
for(size_t i = 0; i < _capacity; i++)
{
pthread_t pid;
int ret = pthread_create(&pid, NULL, start_routine, this);
if(ret)
{
std::cout << "線程創建失敗" << std::endl;
exit(-1);
}
}
}
~ThreadPool()
{
pthread_cond_destroy(&_cond);
pthread_mutex_destroy(&_mutex);
}
void Push(ThreadTask& Task)
{
//互斥鎖保證線程安全
pthread_mutex_lock(&_mutex);
//將任務放入隊列中
_queue.push(Task);
//喚醒線程池全部線程,誰搶到誰就來處理這個任務
pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
//入口函數的參數只能有一個void*,所以需要寫爲static函數來去掉隱含的this指針
static void* start_routine(void *arg)
{
//將void*強轉爲需要的類型
ThreadPool* pool = (ThreadPool*)arg;
while(1)
{
pthread_mutex_lock(&pool->_mutex);
//如果任務隊列爲空則使線程循環等待
while(pool->_queue.empty())
{
pthread_cond_wait(&pool->_cond, &pool->_mutex);
}
ThreadTask Task;
//將任務出隊進行處理
Task = pool->_queue.front();
pool->_queue.pop();
pthread_mutex_unlock(&pool->_mutex);
//解鎖後再處理,因爲加鎖只是保證隊列操作的安全性
Task.Run();
}
return NULL;
}
private:
//互斥鎖保證線程安全,條件變量保證任務的提交與處理同步
pthread_cond_t _cond;
pthread_mutex_t _mutex;
size_t _capacity;
std::queue<ThreadTask> _queue;
};
下面寫一個函數來測試一下
#include "ThreadPool.hpp"
#include<unistd.h>
void handler1(int data)
{
std::cout << "線程ID:"<< pthread_self() <<" 處理奇數數據:" << data << std::endl;
sleep(1);
}
void handler2(int data)
{
std::cout << "線程ID:"<< pthread_self() <<" 處理偶數數據:" << data << std::endl;
sleep(1);
}
int main()
{
ThreadPool pool;
for(int i = 0; i < 10; i++)
{
ThreadTask task;
//分別處理奇數和偶數
if(i & 1)
{
task.SetTask(i, handler1);
}
else
{
task.SetTask(i, handler2);
}
//任務放入隊列中
pool.Push(task);
}
//休眠一段時間防止結束後主線程退出
sleep(100);
return 0;
}
使用兩種不同的處理方法來分別處理0-9的奇偶數
運行截圖