Linux C++ 线程池

头文件定义:

话不多说,先上代码:
注意:本文代码来源于https://github.com/progschj/ThreadPool.git
不需要注释,想要干净代码的可以直接上GitHub去找资源

头文件 线程池类代码
/*
 * Date : 2018/7/24
 * Author : wqw
 * Content : ThreadPool 线程池实现
 * Origin : https://github.com/progschj/ThreadPool.git
 * File : ThreadPool.h
 */
 
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

class ThreadPool{
public:
    ThreadPool(size_t);
    // auto 自动推导类型, Args... 不定形参列表, -> xxx 返回值类型
    // 添加任务
    template <class F, class... Args>
    auto enqueue(F&& f, Args&&... args)
            -> std::future<typename std::result_of<F(Args...)>::type>;
    ~ThreadPool();

private:
    std::vector<std::thread> workers;   // 执行任务的线程容器
    // std::function<> 函数包装, 任务队列
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;             // 互斥锁
    std::condition_variable condition;  // 条件锁
    bool stop;                          // 判断是否停止线程池
};

// 构造函数 开启一定数量的线程
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {
    for (size_t i = 0; i < threads; i++) {
        // 添加工作线程到workers
        workers.emplace_back(
            // lambda 匿名函数
            [this] {
                // 让线程持续工作,
                for (;;) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);   // 互斥锁开启,直到生命周期结束
                        // 若任务列表 tasks 为空,则暂时阻塞线程
                        this->condition.wait(lock,
                                [this]{ return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty())
                            return;
                        task = std::move(this->tasks.front());  // 将tasks最前面的以右值形式赋值给task
                        this->tasks.pop();  // 将tasks刚刚赋值给task的元素弹出
                    }
                    task();  // 让线程执行函数
                }
            }
        );
    }
}

// 添加工作
// 返回值为该工作(即将要执行的任务函数)的返回值
template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
    -> std::future<typename std::result_of<F(Args...)>::type> {
    // 自定义返回值类型 类似于typedef的用法
    using return_type = typename std::result_of<F(Args...)>::type;
    // forward() 保证完美转发,即参数左值右值类型不变
    // std::packaged_task与std::promise相似 用于异步调用,常与std::future共同使用
    // std::bind 包装器 对函数F<Args>进行包装
    // make_shared 智能指针 制作一个智能指针指向包装过的F<Args>
    auto task = std::make_shared<std::packaged_task<return_type()>> (
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
            );
    // std::future 并发编程 绑定task任务
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(this->queue_mutex);
        // 判断ThreadPool是否停止
        if (stop)
            throw std::runtime_error("enqueue on stopped ThreadPool!\n");
        // 添加任务到任务列表
        tasks.emplace([task](){ (*task)(); });
    }
    condition.notify_one(); // 唤醒一个任务
    return res;
}

// 析构函数
inline ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(this->queue_mutex);
        stop = true;
    }
    condition.notify_all();     // 唤醒所有任务
    for (std::thread &worker: workers)
        worker.join();          // 线程自动加上互斥锁执行, detach则是并发执行
}

#endif

调用:

// example 测试代码
/*
 * Date : 2018/7/24
 * Author : wqw
 * Content : ThreadPool 线程池实现类
 * Origin : https://github.com/progschj/ThreadPool.git
 * File : main.cpp
 */

#include <iostream>
#include <vector>
#include <chrono>

#include "Thread_pool.h"


int main()
{

    ThreadPool pool(4);
    std::vector< std::future<int> > results;

    for(int i = 0; i < 8; ++i) {
        results.emplace_back(
                pool.enqueue([i] {
                    std::cout << "hello " << i << std::endl;
                    std::this_thread::sleep_for(std::chrono::seconds(1));
                    std::cout << "world " << i << std::endl;
                    return i*i;
                })
        );
    }

    for(auto && result: results)
        std::cout << result.get() << ' ';
    std::cout << std::endl;

    return 0;
}

线程池的概念:

        线程池是一个类似于银行办理业务的排队系统一样的东西。先开一定数量的线程(如:银行业务窗口),若线程没任何任务则阻塞全部线程,但不关闭(如:银行暂时没人办理业务,但是工作人员总不能下班吧,还是要待机)。若需要执行的任务很多,则我们需要创建一个队列,让队列里的任务先等候,等到有线程空闲时则从队列中取任务(如:银行人很多就要排队一个一个来)。

该代码的线程池的思路:这个线程池类主要由三大部分(三个成员函数)组成:

构造函数 ThreadPool():开启一定数量的线程,每开启一个线程,让该线程进入死循环,对接下来的操作,先利用互斥锁先加锁,再利用条件锁判断线程任务列表是否为空,若为空即阻塞该线程(就是没人办理业务时进入待机状态)。接下来,从任务列表中取任务,然后解开互斥锁,让其执行完后再重复以上操作。对于开启的每一个线程都是如此。

添加任务函数 enqueue():先获取任务,加互斥锁,将该任务加入任务列表,解锁,然后唤醒一个任务,让其进行等待。

析构函数 ~ThreadPool():唤醒所有的工作,让线程一个个的执行。

1. auto 自动推导类型

2. ... 可变模板参数 这篇文章有关其概念使用方法及其参数展开 https://www.cnblogs.com/qicosmos/p/4325949.html

3. function,bind 等是函数包装器,这两个有类似之处都是对函数进行包装

使用方法部分简介可以参考该文章 https://blog.csdn.net/liukang325/article/details/53668046

4. mutex 锁类 详情自行查阅api

5. [] { ... } lambda 匿名函数表达式 此文章有详细的介绍 https://www.cnblogs.com/DswCnblog/p/5629165.html

6. &&,move(),forward()  这里的&&不是指 且 的意思,是右值引用。这里涉及一个左值右值的问题,move()和forward() ,就是围绕左值引用,右值引用而设立的专门的函数。是一个很麻烦但是又挺重要的知识点。

这个文章写的比较通俗易懂 http://www.cnblogs.com/qicosmos/p/4283455.html#3995775

这篇文章是更深入的讲解 http://www.cnblogs.com/catch/p/3507883.html (这篇我都看到迷迷糊糊^.^)

7. future, promise, package_task 这几个是关于线程方面的辅助函数,future与(promise,package)配合使用,用于异步读取,追踪,正在的线程中某些变量。这篇文章有其概念的简介及联系 https://blog.csdn.net/jiange_zh/article/details/51602938

8. make_share 用于制作智能指针 share_ptr C++ primer 6 有介绍。该智能指针是会自动释放内存故比较安全。

该文章有其简单概述及使用 https://blog.csdn.net/yagerfgcs/article/details/72886630

猜你喜欢

转载自blog.csdn.net/sz76211822/article/details/84581800
今日推荐