任务:一个函数接口,及接口对应的参数
封装任务,即利用模板化编程,将不同函数接口及参数包装成一个函数对象,等待调用
线程池:初始化多个线程,来执行任务队列中的各项任务,如果任务队列中无任务,则进行等待,这里使用条件变量进行等待和恢复的操作,避免持续占有独占锁,导致其他任务不能执行。
具体代码如下,代码中给出了各个接口的含义及具体操作的注释:
#include <iostream>
#include <mutex>
#include <queue>
#include <vector>
#include <thread>
#include <windows.h>
#include <functional>
using namespace std;
class Task {
public:
// 任务封装
template<typename FuncType, typename ...ARGS>
Task(FuncType f, ARGS ...args) {
this->func = bind(f, forward<ARGS>(args)...);
}
// 任务执行
void run() { this->func(); }
private:
// 函数对象
function<void()> func;
};
class ThreadPool {
private:
mutex m_mutex; // 互斥锁,保证对任务队列的操作及状态修改是互斥的
bool m_status; // 线程池状态
queue<Task*> m_tasks; // 任务队列,先来先执行
condition_variable m_cond; // 条件变量,用于通知线程任务的到来
vector<thread *> m_threads; // 线程池
// 从任务队列中获取一个任务
Task* GetOneTask() {
// 加锁
unique_lock<std::mutex> lock(m_mutex);
// 使用条件变量,等待任务
while (this->m_status && this->m_tasks.empty()) {
this->m_cond.wait(lock);
}
// 从任务队列中,获取任务
Task* task = nullptr;
if (this->m_status) {
task = this->m_tasks.front();
this->m_tasks.pop();
}
return task;
}
public:
// 初始化线程池
ThreadPool(int n) : m_status(true), m_threads(n) {
for (auto& pt : m_threads) {
pt = new thread(&ThreadPool::Worker, this);
}
}
// 线程执行接口
void Worker() {
// 获取任务,并执行
while (this->m_status) {
Task* task = this->GetOneTask();
if (task == nullptr) break;
task->run();
}
}
// 添加任务到任务队列中
void AddOneTask(Task* task) {
// 临界区加锁
unique_lock<std::mutex> lock(m_mutex);
// 添加任务到任务队列中
this->m_tasks.emplace(task);
// 通知正在等待的线程,获取任务
this->m_cond.notify_one();
}
~ThreadPool() {
// 修改状态参数
this->m_status = false;
// 通知工作线程
unique_lock<std::mutex> lock(m_mutex);
m_cond.notify_all();
lock.unlock();
// 等待线程结束,删除对象
for (int i = 0; i < m_threads.size(); ++i) {
this->m_threads[i]->join();
delete m_threads[i];
}
}
};
// 全局互斥锁,保证同一时刻只有一个线程在执行此函数,其它进入函数的线程会阻塞, 直到锁释放
std::mutex g_mutex;
void myAdd(int a, int b) {
unique_lock<std::mutex> lock(g_mutex);
cout << "task " << a << ": " << a << " + " << b << " = " << a + b << endl;
}
int main() {
// 线程池:初始化10个工作线程
ThreadPool pool(10);
// 任务:创建100个任务, 并将添加到线程池的任务队列中,由线程执行
vector<Task*> tasks(100, nullptr);
for (int i = 0; i < 100; ++i) {
// 创建一个任务
tasks[i] = new Task(myAdd, i, i + 1);
// 将任务放入线程池的任务队列中
pool.AddOneTask(tasks[i]);
}
// 睡眠5s,等待所有任务完成
Sleep(5000); // windows API
// 释放堆区变量
for (int i = 0; i < 100; ++i) {
delete tasks[i];
tasks[i] = nullptr;
}
cout << "End of main()" << endl;
return 0;
}
上述代码在windows下可直接运行,运行结果如下:
task 0: 0 + 1 = 1
task 1: 1 + 2 = 3
task 2: 2 + 3 = 5
task 3: 3 + 4 = 7
task 4: 4 + 5 = 9
task 14: 14 + 15 = 29
task 7: 7 + 8 = 15
task 8: 8 + 9 = 17
task 17: 17 + 18 = 35
task 18: 18 + 19 = 37
task 10: 10 + 11 = 21
task 11: 11 + 12 = 23
task 21: 21 + 22 = 43
task 13: 13 + 14 = 27
task 23: 23 + 24 = 47
task 15: 15 + 16 = 31
task 16: 16 + 17 = 33
task 6: 6 + 7 = 13
task 9: 9 + 10 = 19
task 19: 19 + 20 = 39
task 20: 20 + 21 = 41
task 12: 12 + 13 = 25
task 22: 22 + 23 = 45
task 5: 5 + 6 = 11
task 24: 24 + 25 = 49
task 25: 25 + 26 = 51
task 26: 26 + 27 = 53
task 27: 27 + 28 = 55
task 28: 28 + 29 = 57
task 29: 29 + 30 = 59
task 30: 30 + 31 = 61
task 40: 40 + 41 = 81
task 32: 32 + 33 = 65
task 33: 33 + 34 = 67
task 34: 34 + 35 = 69
task 35: 35 + 36 = 71
task 36: 36 + 37 = 73
task 37: 37 + 38 = 75
task 38: 38 + 39 = 77
task 39: 39 + 40 = 79
task 31: 31 + 32 = 63
task 41: 41 + 42 = 83
task 42: 42 + 43 = 85
task 43: 43 + 44 = 87
task 44: 44 + 45 = 89
task 45: 45 + 46 = 91
task 46: 46 + 47 = 93
task 47: 47 + 48 = 95
task 48: 48 + 49 = 97
task 49: 49 + 50 = 99
task 50: 50 + 51 = 101
task 51: 51 + 52 = 103
task 52: 52 + 53 = 105
task 53: 53 + 54 = 107
task 54: 54 + 55 = 109
task 55: 55 + 56 = 111
task 56: 56 + 57 = 113
task 57: 57 + 58 = 115
task 67: 67 + 68 = 135
task 59: 59 + 60 = 119
task 60: 60 + 61 = 121
task 61: 61 + 62 = 123
task 62: 62 + 63 = 125
task 72: 72 + 73 = 145
task 64: 64 + 65 = 129
task 65: 65 + 66 = 131
task 66: 66 + 67 = 133
task 76: 76 + 77 = 153
task 68: 68 + 69 = 137
task 69: 69 + 70 = 139
task 70: 70 + 71 = 141
task 71: 71 + 72 = 143
task 63: 63 + 64 = 127
task 73: 73 + 74 = 147
task 74: 74 + 75 = 149
task 75: 75 + 76 = 151
task 58: 58 + 59 = 117
task 77: 77 + 78 = 155
task 78: 78 + 79 = 157
task 79: 79 + 80 = 159
task 80: 80 + 81 = 161
task 81: 81 + 82 = 163
task 82: 82 + 83 = 165
task 83: 83 + 84 = 167
task 84: 84 + 85 = 169
task 85: 85 + 86 = 171
task 95: 95 + 96 = 191
task 87: 87 + 88 = 175
task 88: 88 + 89 = 177
task 89: 89 + 90 = 179
task 90: 90 + 91 = 181
task 91: 91 + 92 = 183
task 92: 92 + 93 = 185
task 93: 93 + 94 = 187
task 94: 94 + 95 = 189
task 86: 86 + 87 = 173
task 96: 96 + 97 = 193
task 97: 97 + 98 = 195
task 98: 98 + 99 = 197
task 99: 99 + 100 = 199
End of main()
大部分情况下,每次运行结果都不会相同,因为任务到达和执行的时间不是确定的。
参考链接:08 | 封装线程池(下)
谢谢阅读。