线程池的学习

//案例是4个线程,8个任务
1)线程池头文件

#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include
#include
#include
#include
#include
#include
#include
#include

//共计3个函数,5个变量;
class ThreadPool
{
public:
    ThreadPool(size_t);
    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::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)
    {
        //利用两个lambda表达式
        workers.emplace_back(
            [this]
            {
                for(;;)
                {
                    std::function<void()> task;
                    { //这个大括号主要是为了加锁设置的作用域权限
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        //即其表示若线程池已停止或者任务队列中不为空,则不会进入到wait状态;
                        //说白了就是任务队列中有任务,就不能等待!!!
                        this->condition.wait(lock, [this]{return this->stop || !this->tasks.empty();} ); //可简写returnstop || !tasks.empty();
                        if(stop && tasks.empty()) //若线程池已经停止且任务队列为空,则线程返回,没必要进行死循环
                        {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task(); //任务运行起来
                }
            }
        );
    }
}

template<class F,class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
->std::future<typename std::result_of<F(Args...)>::type> // ->std::future<>中,表示返回类型,其类型是:typename std::result_of::type
{
    using return_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<return_type()>>
    (
        std::bind(std::forward<F>(f),std::forward<Args>(args)...)
    );

    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        if(stop)
        {
            throw std::runtime_error("enqueue on stopped threadpool");
        }
        tasks.emplace([task](){(*task)();});
        condition.notify_one();
        return res; //最终返回的是放在std::future中的F(Args…)返回类型的异步执行结果
    }
}

inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::thread& worker : workers)
    {
        worker.join();
    }
}

#endif

2)线程池测试文件

#include 
#include 
#include 
 
#include "threadpool.h"
using namespace std;

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<<endl;
                    std::this_thread::sleep_for(std::chrono::seconds(1));
                    std::cout<<"world---: "<<i<<endl;
                    return i*i;
                }
            )
        );
    }

    for(auto&& result : results)
    {
        std::cout<<"result is: "<<result.get()<<endl;
    }
    std::cout<<std::endl;
    return 0;
}

三)对上面代码的分析
3个成员函数
ThreadPool(size_t) 线程池的构造函数
auto enqueue(F&& f, Args&&… args) 将任务添加到线程池的任务队列中
~ThreadPool() 线程池的析构函数
5个成员变量
std::vector< std::thread > workers 用于存放线程的数组,用vector容器保存
std::queue< std::function > tasks 用于存放任务的队列,用queue队列进行保存。任务类型为std::function
因为std::function是通用多态函数封装器,也就是说本质上任务队列中存放的是一个个函数
std::mutex queue_mutex 一个访问任务队列的互斥锁,在插入任务或者线程取出任务都需要借助互斥锁进行安全访问
std::condition_variable condition 一个用于通知线程任务队列状态的条件变量,若有任务则通知线程可以执行,否则进入wait状态
bool stop 标识线程池的状态,用于构造与析构中对线程池状态的了解;

四)对任务队列及线程池的分析
由于刚开始创建线程池,线程池表示未停止,且任务队列为空,所以每个线程都会进入到wait状态;
线程池的学习_第1张图片

你可能感兴趣的:(学习)