本文使用C++11的線程、互斥量和條件變量,實現了一個輕巧的線程池,可用於大量併發任務的場景,以避免頻繁的線程創建和銷燬,節約系統資源。
本文對處理任務的接口,也進行了包裝,可接受:函數對象、成員函數、普通函數、fucntion和lamda表達式,便於完美的應用到實際工作中。
本文源碼見【完整代碼】章節,或GitHub:https://github.com/deargo/cpphelper。
線程池介紹
在處理大量併發任務的時候,如果按照傳統的方式,一個請求一個線程來處理請求任務,大量的線程創建和銷燬,將消耗過多的系統資源,還增加了線程上下文切換的開銷,而通過線程池技術,就可以很好的解決這些問題。
線程池技術通過在系統中預先創建一定數量的線程,當任務請求到來時從線程池中,分配一個預先創建的線程去處理任務,線程在完成任務之後,還可以重用,不會銷燬,而是等待下次任務的到來。
線程池分爲半同步半異步線程池和領導者追隨者線程池,本文將介紹的是前者。半同步半異步線程池分爲三層:
- 同步服務層:它處理來自上層的任務請求,上層的請求可能是併發的,這些請求不是馬上就會被處理的,而是將這些任務放到一個同步排隊層中,等待處理。
- 同步排隊層:來自上層的任務請求,都會加到排隊層中,等待處理。
- 異步服務層:這一層中會有多個線程,同時處理排隊層中的任務,異步服務層從同步排隊層中取出任務,並行的處理。
關鍵技術分析
通過上一節我們知道,排隊層居於核心地位,因爲上層會將任務添加到排隊層中,下層同時也會取出任務,這裏有一個同步的過程。
在實現時,排隊層就是一個同步隊列,允許多個線程同時去添加,或取出任務,並且要保障操作過程是安全的。
線程池有兩個活動過程,一個是往同步隊列中添加任務的過程,另一個是從同步隊列中取出任務的過程。
線程池需啓動一定數量的異步線程,來並行處理排隊層中的任務,如果排隊層中的任務爲空,則需等待任務的到來,如果發現有新任務,則會喚醒一個線程來處理。
同步服務層會不斷的將新的任務,添加到同步排隊層中。當任務非常多又耗時時,異步線程處理不過來,則會導致排隊層任務過多,內存暴漲,因此需要添加上限,避免此問題。
另外,對於任務類型,與線程池,本是不相關的,但考慮到實際運用,任務類型應支持:函數對象、成員函數、普通函數、fucntion和lamda表達式等,於是結合我的另外一篇博客《C++Helper--用C++11改進命令模式》,可對添加任務的接口,進行優化。
完整代碼
#pragma once
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include <list>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
using namespace std;
namespace CppHelper
{
class CThreadPool
{
private:
using Locker = std::unique_lock<std::mutex>;
using Guard = std::lock_guard<std::mutex>;
//同步隊列
template <typename Task>
class SyncQueue
{
public:
using Tasks = std::list<Task>;
SyncQueue(int maxSize): m_maxSize(maxSize), m_needStop(false) { }
~SyncQueue(){ resize(0);}
//添加事件
bool put(const Task& task)
{
return add(task);
}
//添加事件
bool put(Task && task)
{
//調用內部接口,進行完美轉發
return add(std::forward<Task>(task));
}
//從隊列中取事件,取所有事件
bool take(Tasks &tasks)
{
Locker locker(m_mutex);
//當不滿足任何一個則等待,但是若m_needStop爲true是因爲任務要終止了所以不阻塞
m_notEmpty.wait(locker, [this]{return (m_needStop || notEmpty()); });
if (m_needStop)
{
return false;
}
tasks = std::move(m_tasks);
m_notFull.notify_one();
return true;
}
//取一個事件
bool take(Task &task)
{
Locker locker(m_mutex);
m_notEmpty.wait(locker, [this]{return m_needStop || notEmpty(); });
if (m_needStop)
{
return false;
}
task = m_tasks.front();
m_tasks.pop_front();
m_notFull.notify_one();
return true;
}
//終止同步隊列
void stop()
{
{
//鎖作用域就在這對大括號內
Guard guard(m_mutex);
//將終止標誌設爲true
m_needStop = true;
}
//喚醒所有進程一一終止
m_notFull.notify_all();
m_notEmpty.notify_all();
}
//隊列爲空
bool empty()
{
Guard guard(m_mutex);
return m_tasks.empty();
}
//隊列爲滿
bool full()
{
Guard guard(m_mutex);
return m_tasks.size() == m_maxSize;
}
//隊列容量
typename Tasks::size_type capacity()
{
Guard guard(m_mutex);
return m_maxSize;
}
//隊列大小
typename Tasks::size_type size()
{
Guard guard(m_mutex);
return m_tasks.size();
}
//重置隊列大小
void resize(int maxSize)
{
stop();
Locker locker(m_mutex);
m_tasks.clear();
m_maxSize = maxSize;
}
//清空隊列
void clear()
{
stop();
Locker locker(m_mutex);
m_tasks.clear();
}
private:
//隊列不爲滿
bool notFull() const
{
bool full = (m_tasks.size() >= m_maxSize);
if (full)
{
cout << "the queue is full, need wait..." << endl;
}
return !full;
}
//隊列不爲空
bool notEmpty() const
{
bool empty = m_tasks.empty();
if (empty)
{
cout << "asynchronous thread["<<this_thread::get_id() << "]: the queue is empty, need wait." << endl;
}
return !empty;
}
//向隊列中添加任務,若不爲滿且終止標誌爲false則添加事件
bool add(Task && task)
{
Locker locker(m_mutex);
//當不滿足任何一個則等待,但是若m_needStop爲true是因爲任務要終止了所以不阻塞
m_notFull.wait(locker, [this]{return m_needStop || notFull(); });
if (m_needStop)
{
return false;
}
m_tasks.emplace_back(std::forward<Task>(task));
m_notEmpty.notify_one();
return true;
}
private:
//緩衝區
Tasks m_tasks;
//互斥量和條件變量結合起來使用
std::mutex m_mutex;
//隊列不爲空的條件變量
std::condition_variable m_notEmpty;
//隊列不爲滿的條件變量
std::condition_variable m_notFull;
//同步隊列最大長度
size_t m_maxSize;
//終止的標識,當爲true時代表同步隊列要終止
bool m_needStop;
};
private:
using Ret = void;
using Task = std::function<Ret()>;
using Tasks = std::list<Task>;
public:
//傳遞給同步隊列的最大個數
enum { DEFAULT_MAX_TASK_COUNT = 1000 };
//構造函數,默認處理線程數爲CPU核心數量
CThreadPool(int handleTheadCount = std::thread::hardware_concurrency()):
m_handle_count(handleTheadCount),m_queue(CThreadPool::DEFAULT_MAX_TASK_COUNT)
{
start();
}
~CThreadPool()
{
//如果沒有停止,則主動停止線程池
stop();
}
//啓動線程池(默認已啓動)
void start()
{
Locker locker(m_mutex);
if(m_running)
{
return;
}
run();
}
//終止所有任務的執行
void stop()
{
Locker locker(m_mutex);
if(false == m_running)
{
return;
}
//終止同步隊列
m_queue.stop();
m_running = false;
for (auto thread : m_threadgroup)
{
if (thread)
{
thread->join();
}
}
m_threadgroup.clear();
}
//添加任務:接受返回類型爲void類型的function、函數對象、lamda和普通函數
template< class Func, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<Func>::value>::type>
bool addTask(Func && func, Args && ... args)
{
return m_queue.put([&func, args...]{return func(args...); });
}
//添加任務:接受返回類型爲void類型的const成員函數
template<class CObj, class... DArgs, class PObj, class... Args>
bool addTask(Ret(CObj::*func)(DArgs...) const, PObj && pObj, Args && ... args)
{
return m_queue.put([&, func]{return (*pObj.*func)( args...); });
}
//添加任務:接受返回類型爲void類型的non-const類型成員函數
template<class CObj, class... DArgs, class PObj, class... Args>
bool addTask(Ret(CObj::*func)(DArgs...), PObj && pObj, Args && ... args)
{
return m_queue.put([&, func]{return (*pObj.*func)( args...); });
}
//隊列接口
bool queueEmpty() { return m_queue.empty();}
bool queueFull() { return m_queue.full();}
typename Tasks::size_type queueCapacity() { return m_queue.capacity();}
typename Tasks::size_type queueSize() { return m_queue.size();}
void queueResize(int maxSize) { return m_queue.resize(maxSize);}
void queueClear() { return m_queue.clear();}
//處理接口
bool isRunning() { Guard guard(m_mutex); return m_running;}
int handleCount() {Guard guard(m_mutex); return m_handle_count;}
void handleCountResize(int handleTheadCount)
{
stop();
Locker locker(m_mutex);
m_handle_count = handleTheadCount;
run();
}
private:
void run()
{
m_running = true;
//建立numThreads個數的線程組
for (int i = 0; i < m_handle_count; i++)
{
//多個線程依次的處理
m_threadgroup.emplace_back(std::make_shared<std::thread>(&CThreadPool::runInThread, this));
}
}
void runInThread()
{
while (m_running)
{
Tasks list;
m_queue.take(list);
//取出任務隊列中的全部,依次執行
for (auto & task : list)
{
if (!m_running)
{
return ;
}
//執行任務
task();
}
}
return;
}
private:
std::mutex m_mutex;
int m_handle_count;
//處理任務的線程組
std::list<std::shared_ptr<std::thread>> m_threadgroup;
//同步隊列
SyncQueue<Task> m_queue;
//運行的標誌,flase代表終止
atomic_bool m_running;
//保證在函數在多線程環境中只被調用一次
std::once_flag m_flag;
};
}
測試示例
在本示例中,線程池初始將創建兩個線程,然後外部線程將不停的向線程池中,添加新任務,線程池中的異步線程,將會並行的處理同步隊列中的任務。
測試代碼
CppHelper::CThreadPool pool(2);
std::thread thd1([&pool]
{
auto thdid = this_thread::get_id();
for (int i = 0; i < 10; i++)
{
pool.addTask([](std::thread::id thdid, int value)
{
cout << "synchronous thread1[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
},thdid,i+100);
}
});
std::thread thd2([&pool]
{
auto thdid = this_thread::get_id();
for (int i = 0; i < 10; i++)
{
pool.addTask([&](std::thread::id thdid, int value)
{
cout << "synchronous thread2[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
},thdid,i+200);
}
});
cout << "sleep seconds(2) ..." << endl << endl;
this_thread::sleep_for(std::chrono::seconds(2));
cout << endl << "input any words to stop: ";
getchar();
pool.stop();
thd1.join();
thd2.join();
測試結果
參考資料及源碼:
參考書籍:《深入應用C++11:代碼優化與工程級應用》。