CURD を作成したプログラマは、「データベース接続プール」についてよく知っている必要があります。データベース接続は、その作成プロセスにより多くのリソースが消費されるため、コストがかかります。そこで、リソースを節約し、データベース操作のパフォーマンスを向上させるために、「データベース接続プール」が登場しました。
実は、スレッド プールはデータベース接続プールと似ており、スレッドの作成と削除にもリソースが消費され、スレッドの作成と削除を繰り返すと、リソースの浪費も大きくなります。したがって、事前にいくつかのスレッドを作成し、それらを対応するスレッドに割り当てて、タスクがあるときに実行することは、システムのパフォーマンスの向上に非常に良い影響を与えます。
1. スレッドプールの作成プロセス
1. スレッドを作成する
for (size_t i = 0; i < threadCount; ++i) {
threads.emplace_back(threadFunc, this);
}
スレッドはベクトルなので、最初にいくつかのスレッドを作成し、これらのスレッドをベクトルに入れることができます。
2. タスクキューとスケジューリング
void ThreadPool::addTask(const Task& task) {
{
lock_guard<mutex> lock(queueMutex);
taskQueue.emplace(task);
}
condition.notify_one();
}
新しく受信したタスクは taskQueue キューに追加され、特定の戦略に従って taskQueue からタスクを取り出し、アイドル状態のスレッドに渡して実行することができます。
3. スレッドの実行とリサイクル
void ThreadPool::threadFunc() {
while (true) {
Task task;
{
unique_lock<mutex> lock(queueMutex);
//wait()用来等一个东西
//1. 如果第2个参数为false,就跟只有1个参数一样,直接阻塞到本行
//阻塞到什么时候为止呢?有其他持有锁的线程notify()后.
//2. 当wait()被notify()唤醒后,会先判断第2个参数是否为true,如果为true才会
//继续往下执行.
condition.wait(lock, [this]() { return !taskQueue.empty() || terminate; });
if (terminate && taskQueue.empty()) {
break;
}
task = taskQueue.front();
taskQueue.pop();
}
task(); // Execute the task.
}
}
4. スレッドプールの正常な終了
マルチスレッド プログラムでは、スレッドをどのように終了するかは「慎重な」検討が必要な問題ですが、実行中のスレッドが突然中断され、内部データの不整合が発生するため、スレッドを直接停止することはできません。
最初にフラグを追加する必要があります: terminate. terminate フラグが true の場合、まずスレッド プールの while タスクを停止し、次に待機中のすべてのスレッドをウェイクアップし、std::thread::join() 関数を使用して待機します。実行を終了するスレッド。
ThreadPool::~ThreadPool() {
terminate = true;
condition.notify_all(); // 唤醒所有等待中的线程
for (thread& th : threads) {
if (th.joinable()) {
th.join(); // 等待线程执行完毕
}
}
}
2. スレッドプールの例
#include <vector>
#include <queue>
#include <thread>
#include <iostream>
#include <stdexcept>
#include <condition_variable>
#include <memory>
#define MAX_THREADS 8 //最大线程数目
class Task{
public:
void process(){
}
};
class ThreadPool {
public:
ThreadPool() = default;
ThreadPool(const ThreadPool &) = delete;
ThreadPool(ThreadPool &&) = delete;
ThreadPool &operator = (const ThreadPool &) = delete;
ThreadPool &operator = (ThreadPool &&) = delete;
//初始化所有线程并启动线程
void init(int threadCount){
if (threadCount <= 0 || threadCount > MAX_THREADS) {
throw std::exception();
}
for (int i = 0; i < threadCount; i++) {
// emplace_back不能先构造再插入.
// 方法2: std::thread temp(worker, this); work_threads.push(temp);
work_threads.emplace_back(worker, this);
}
}
bool dispatch(std::shared_ptr<Task>&& request){
// 操作工作队列时一定要加锁, 因为他被所有线程共享
std::unique_lock<std::mutex> lock(queue_mutex);
lock.lock();
tasks_queue.push(request);
lock.unlock();
// 线程池添加进去了任务, 通知等待的线程
condition.notify_one();
return true;
}
~ThreadPool(){
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
condition.notify_all();
for (auto& w : work_threads) {
w.join();
}
}
private:
// 工作线程需要运行的函数,不断的从任务队列中取出并执行
static void* worker(void* arg){
ThreadPool* pool = (ThreadPool*)arg;
pool->run();
return pool;
}
void run(){
while (!stop)
{
// unique_lock() 出作用域会自动解锁
std::unique_lock<std::mutex> lk(this->queue_mutex);
// 如果任务队列不为空,就停下来等待唤醒
this->condition.wait(lk, [this] {
return !this->tasks_queue.empty();
});
std::shared_ptr<Task> request = tasks_queue.front();
tasks_queue.pop();
if (request) {
request->process();
}
}
}
private:
// 工作线程
std::vector<std::thread> work_threads;
// 任务队列
std::queue<std::shared_ptr<Task>> tasks_queue;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop = false;
};
int main(){
ThreadPool pool;
pool.init(4);
return 0;
}
参考: