本篇文章用来学习和实现轻松掌握C++线程池:从底层原理到高级应用。
代码如下:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <queue>
#include <atomic>
#include <chrono>
using namespace std;
struct Task {
int priority;
void operator()() {
std::cout << "Task executed with priority " << priority << std::endl;
}
};
//优先队列弹出元素时从堆顶往下弹,按照下面的写法,priority大的在顶部,先弹出。
struct LessByPriority {
bool operator()(const Task& lhs, const Task& rhs) const {
return lhs.priority < rhs.priority;
}
};
class ThreadPool {
public:
ThreadPool(size_t threadCount) : terminate(false), threadCount(threadCount), taskCount(0), completedTaskCount(0), startTime(chrono::steady_clock::now()) {
for (size_t threadNum = 0; threadNum < threadCount; ++threadNum) {
//成员函数指针,必须在前面加&,普通函数若想表示函数指针,可加&也可以不加。学一下构造thread的几种方法。
threads.emplace_back(&ThreadPool::threadFunc, this, threadNum);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
~ThreadPool() {
terminate = true;
condition.notify_all();
for (thread& th : threads) {
if (th.joinable()) {
th.join();
}
}
}
void addTask(const Task& task) {
{
taskQueue.emplace(task);
taskCount++;
}
condition.notify_one();//每往任务队列中加入一个任务,就通知线程池处理一个
}
size_t getCompletedTaskCount() const {
return completedTaskCount.load();
}
double getRunningTimeInSeconds() const {
chrono::duration<double> duration = chrono::steady_clock::now() - startTime;
return duration.count();
}
private:
void threadFunc(int threadNum) {
std::cout << "threadFunc " << threadNum << " is ready" << "\n";
while (true) {
Task task;
{
unique_lock<mutex> lock(queueMutex);
//几个线程都在这里排队等待,当外部调用condition.notify_one();时,操作系统一般是调用排队的最前面的线程
condition.wait(lock, [this]() { return!taskQueue.empty() || terminate; });
if (terminate && taskQueue.empty()) {
break;
}
task = taskQueue.top();
taskQueue.pop();
}
task(); // Execute the task.
std::cout << "Execute the task with threadNum " << threadNum << "\n";
completedTaskCount++;
}
}
std::vector<std::thread> threads;
//定义优先队列来存任务,底层容器选择vector,通过比较字段priority来决定优先级
std::priority_queue<Task, std::vector<Task>, LessByPriority> taskQueue;
std::mutex queueMutex;
std::condition_variable condition;
public:
//不需要定义原子操作,直接访问更简洁
size_t threadCount;
size_t taskCount;
private:
//定义原子变量,保证线程安全
atomic<bool> terminate;
atomic<size_t> completedTaskCount;
chrono::steady_clock::time_point startTime;
};
int main() {
ThreadPool pool(5);
std::cout << "ThreadPool pool OK" << "\n\n";
for (int i = 0; i < 10; ++i) {
Task task;
task.priority = i;
pool.addTask(task);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "\n";
std::cout << "Thread count: " << pool.threadCount << std::endl;
std::cout << "Task count: " << pool.taskCount << std::endl;
std::cout << "Completed task count: " << pool.getCompletedTaskCount() << std::endl;
std::cout << "Running time: " << pool.getRunningTimeInSeconds() << " seconds" << std::endl;
return 0;
}
执行时要加-pthread,结果是:
$ g++ -pthread 线程池.cpp
$ ./a.out
threadFunc 0 is ready
threadFunc 1 is ready
threadFunc 2 is ready
threadFunc 3 is ready
threadFunc 4 is ready
ThreadPool pool OK
Task executed with priority 0
Execute the task with threadNum 0
Task executed with priority 1
Execute the task with threadNum 1
Task executed with priority 2
Execute the task with threadNum 2
Task executed with priority 3
Execute the task with threadNum 3
Task executed with priority 4
Execute the task with threadNum 4
Task executed with priority 5
Execute the task with threadNum 0
Task executed with priority 6
Execute the task with threadNum 1
Task executed with priority 7
Execute the task with threadNum 2
Task executed with priority 8
Execute the task with threadNum 3
Task executed with priority 9
Execute the task with threadNum 4
Thread count: 5
Task count: 10
Completed task count: 10
Running time: 8.50287 seconds
注:
①关于优先队列,task本身没有定义顺序。这里每次队列里有一个任务时就处理了,并未体现出多个任务需要调度时,按优先级高的先进行处理。但这么定义排序的写法是没错的。见下:
#include <iostream>
#include <queue>
#include <vector>
// 自定义比较函数 LessByPriority
struct LessByPriority {
bool operator()(int a, int b) {
return a < b; // 优先级低的排在前面,如果要优先级高的排在前面,则改为 a < b
}
};
int main() {
std::priority_queue<int, std::vector<int>, LessByPriority> pq;
// 逐个插入元素
pq.push(5);
pq.push(7);
pq.push(2);
pq.push(9);
pq.push(3);
// 输出队列中排好序的结果
std::cout << "Priority Queue sorted result: ";
while (!pq.empty()) {
std::cout << pq.top() << " ";
pq.pop();
}
std::cout << std::endl;
return 0;
}
结果是:
Priority Queue sorted result: 9 7 5 3 2
②代码中的threads.emplace_back不可以替代为thread.push_back。
因为push_back 接受容器的元素类型的对象,并将其拷贝(左值)或移动(右值)到容器的末尾。
而emplace_back接受构造函数的参数,并在容器的末尾直接构造一个新元素,而不需要创建临时对象。
括号里的内容是参数,不是对象。