使用C++实现线程池

任务:一个函数接口,及接口对应的参数

封装任务,即利用模板化编程,将不同函数接口及参数包装成一个函数对象,等待调用

线程池:初始化多个线程,来执行任务队列中的各项任务,如果任务队列中无任务,则进行等待,这里使用条件变量进行等待和恢复的操作,避免持续占有独占锁,导致其他任务不能执行。

具体代码如下,代码中给出了各个接口的含义及具体操作的注释:

#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 | 封装线程池(下)

谢谢阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值