C++实现线程池

C++实现线程池

背景

  • 线程池,或者任务队列是多线程环境下的一种重要的基础组件

功能需求

  • 实现一个任务队列,用来存放当前的任务
  • 当有任务送入时,送入任务队列并提示所有线程,处理库存任务

涉及概念

  • 主要使用了C++11新增的多线程库中的内容;没接触过的话可能比较难以理解,尤其是future\package_task\result_of,除了result_of是用来进行类型推导的,其他主要用于线程的同步,具体可见C++并发指南系列博客
  • std::future
  • std::thread
  • std::package_task
  • std::mutex
  • std::condition_variable
  • std::result_of
  • std::unique_lock

类设计

  • 类名:ThreadPool
  • 成员变量:
    • std::vector workers:线程池中的线程
    • std::queue

代码实现

  • ThreadPool.h
#pragma once
#include <future>
#include <vector>
#include <queue>

class ThreadPool
{
public:
    ThreadPool(size_t); // construct

    template <typename F, typename ... 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)
    {
        workers.emplace_back(
            [this]
        {
            for (;;)
            {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(this->queue_mutex);
                    this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                    if (this->stop&&this->tasks.empty())
                    {
                        return;
                    }
                    task = std::move(this->tasks.front());
                    this->tasks.pop();
                }
                task();
            }
        }
        );

    }
}

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

template <typename F, typename ... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::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 stopping the pool");
        }
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
}

使用

  • 使用示例:
#include "ThreadPool.h"
#include <vector>
#include <iostream>
#include <chrono>

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::this_thread::sleep_for(std::chrono::seconds(1));
                return i*i;
            })
        );
    }
    std::this_thread::sleep_for(std::chrono::seconds(3));
    for (auto && result : results)
    {
        std::cout << result.get() << ' ';
    }
    std::cout << std::endl;
    system("pause");
    return 0;
}

总结

  • 线程池的实现本质上并不复杂,需要注意各个操作的原子性
  • 本实现的特点是完全使用C++11的现代标准库进行实现,涉及了库、lambda表达式、模板编程等概念,虽然代码短小,但是要完全理解并不容易
  • 通过模仿、实现、测试,进一步理解,确实收获很多,所以将自己的学习经验分享出来,供来者鉴
阅读更多

没有更多推荐了,返回首页