线程池的工作原理大概如下:
- 在构造函数中,创建指定数量的工作线程,并将它们存储在
workers
向量中。 - 每个工作线程都通过无限循环来执行任务队列中的任务。
- 设置一个
public
函数,接收新任务,封装为std::function<void()>
并添加到任务队列中。 - 等待的工作线程会被唤醒并获取任务执行,直到线程池被销毁或调用
stop()
函数停止。
第一步, 创建指定数量的工作线程:
class ThreadPool {
public:
ThreadPool(int num){
// 启动指定数量的线程
for(int i = 0; i < num; i++ ) {
// 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
// 添加一个lambda函数即为创建一个线程
workers.emplace_back([this]{
// code...
});
}
}
private:
// 用向量储存线程
std::vector<std::thread> workers;
}
第二步,在每个线程中启动死循环,依次从任务队列中取中任务,直到队列为空或设置stop
标识为true;
在第一步基础上,添加代码如下
class ThreadPool {
public:
ThreadPool(int num): stoped(false) {
// 启动指定数量的线程
for(int i = 0; i < num; i++ ) {
// 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
// 添加一个lambda函数即为创建一个线程
workers.emplace_back([this]{
// 死循环
while(true) {
// 设置一个task接收从队列中取出的任务
std::function<void()> task;
{
// 从队列中取任务要加锁,以避免重复读
std::unique_lock<std::mutex> lock(ququeMutex);
// 如果stoped==true,或任务不为空,则使当前线程等待
condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
// 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
if (stoped && tasks.empty()) {
return;
}
// 从队列中接收任务
task = std::move(tasks.front());
// 弹出已经接收的任务
tasks.pop();
}
// 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
}
});
}
}
// 设置停止标识
void stop() {
std::unique_lock<std::mutex> lock(queueMutex);
stoped = true;
}
private:
// 用向量储存线程
std::vector<std::thread> workers;
// 工作队列
std::queue<std::function<void()> tasks;
// 队列互斥锁
std::mutex queueMutex;
// 条件变量
std::condition_variable condition;
// stop标识
bool stoped;
}
第三步,设置一个函数用从外部接收任务
// class ThreadBool...
template<class F>
void enqueue(F&& f){
{
// 在域内启用锁, 保证加任务时没冲突
std::unique_lock<std::mutex> lock(queueMutex);
// 转发任务到tasks队列中
tasks.emplace(std::forword<F>(f));
}
// 通知一个线程起来接活
condition.notify_one();
}
// ...
第四步:设置析构函数等待所有线程执行完成
// class ThreadBool
~ThreadPool(){
{
std::unique_lock<std::mutex> lock(queueMutex);
stoded = true;
}
// 阻塞等待所有线程
for(std::thread& worker: workers){
worker.join();
}
}
最后完整代码如下:
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <iostream>
thread_local int thr_num;
class Task{
public run(int num = 0) {
thr_num = num;
std::cout << "当前任务号是" << thr_num << std::endl;
}
}
class ThreadPool {
public:
ThreadPool(int num): stoped(false) {
// 启动指定数量的线程
for(int i = 0; i < num; i++ ) {
// 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
// 添加一个lambda函数即为创建一个线程
workers.emplace_back([this]{
// 死循环
while(true) {
// 设置一个task接收从队列中取出的任务
std::function<void()> task;
{
// 从队列中取任务要加锁,以避免重复读
std::unique_lock<std::mutex> lock(ququeMutex);
// 如果stoped==true,或任务不为空,则使当前线程等待
condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
// 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
if (stoped && tasks.empty()) {
return;
}
// 从队列中接收任务
task = std::move(tasks.front());
// 弹出已经接收的任务
tasks.pop();
}
// 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
}
});
}
}
// 设置停止标识
void stop() {
std::unique_lock(std::mutex )
stoped = true;
}
private:
// 用向量储存线程
std::vector<std::thread> workers;
// 工作队列
std::queue<std::function<void()> tasks;
// 队列互斥锁
std::mutex queueMutex;
// 条件变量
std::condition_variable condition;
// stop标识
bool stoped;
}
int main() {
ThreadPool tp(4);
Task task;
for(int i = 0; i<4; i++) {
int num = i;
tp.enqueue([num, &task] {
task.run(num);
});
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。