前面已經說到了同步隊列的實現,下面來看線程池的實現。
#ifndef INCLUDE_THREADPOOL__
#define INCLUDE_THREADPOOL__
#include <list>
#include <thread>
#include <memory>
#include <atomic>
#include "SyncQueue.hpp"
namespace MyThreadPool {
class ThreadPool {
using Task = std::function<void()>;
public:
ThreadPool(int max_size = std::thread::hardware_concurrency())
: sync_queue_{max_size} {
running_ = true;
for (int i = 0; i < max_size; ++i) {
threads_.emplace_back(
std::make_shared<std::thread>(&ThreadPool::Run, this));
}
}
~ThreadPool() {
Stop();
}
void AddTask(Task&&task) {
sync_queue_.Push(std::forward<Task>(task));
}
private:
void Run() {
while (running_) {
Task task{[]{}};
sync_queue_.Pop(task);
task();
}
}
void Stop() {
sync_queue_.Clear();
running_ = false;
for (int i = 0; i < threads_.size(); ++i) {
AddTask([]{});
}
for (auto &thread : threads_) {
if (thread && thread->joinable()) {
thread->join();
}
}
threads_.clear();
}
std::list<std::shared_ptr<std::thread>> threads_;
std::atomic_bool running_;
SyncQueue<Task> sync_queue_;
};
}
#endif // INCLUDE_THREADPOOL__
可以看到,ThreadPool構造的時候就預先創建了一些線程,線程數量默認爲cpu核數。線程會從同步隊列中取出任務,然後執行。同步隊列爲空的話,線程將會處於等待狀態。通過AddTask接口,上層可以將任務添加到同步隊列中,這個時候就喚醒等待獲取任務的隊列。
下面是一個sample:
#include "SyncQueue.hpp"
#include "ThreadPool.hpp"
#include <thread>
#include <iostream>
void task1() {
std::cout << "task1 start" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "task1 finish" << std::endl;
}
void task2() {
std::cout << "task2 start" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "task2 finish" << std::endl;
}
void task3() {
std::cout << "task3 start" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(8));
std::cout << "task3 finish" << std::endl;
}
int main() {
MyThreadPool::ThreadPool thread_pool(3);
thread_pool.AddTask(task1);
thread_pool.AddTask(task2);
thread_pool.AddTask(task3);
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
可以看到main函數最後添加了一個休眠,主要是爲了確保在線程啓動之前,主線程不會退出。
其實這裏添加的任務還是比較簡單的,實際的情況會複雜一些,例如要求任務會有輸出值,還會有返回值,這樣目前確實不滿足。但是我們可以稍微修改一下AddTask函數,結合std::future,在任務結束後獲取返回值。