C++ スレッド プール (1) 理論的基礎と簡単な実装

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

参考:

(1) C++11ベースのスレッドプールの実装

(2) C++ スレッド プールを簡単にマスター: 基礎となる原理から高度なアプリケーションまで

おすすめ

転載: blog.csdn.net/mars21/article/details/131461582