c++11线程池

329 阅读2分钟

背景

c++11加入了线程库std::thread,很好的解决了c++跨平台创建线程等繁琐的问题,但对于多线程的应用还是比较低级的,稍微高级的用法都需要自己去实现。比如场景线程池操作。

我们期望一个线程池具有以下特性:

  • 线程循环利用
  • 线程执行函数可以拥有不同类型或不同数量的参数
  • 线程执行函数可以为类的成员函数或普通函数
  • 可以查询任务执行状态,并且可获得任务执行的结果
  • 可以同步等待任务执行完毕

实现

根据c++11的新特性,包括std::thread、std::function、std::mutex、std::condition_variable、std::atomic、std::packaged_task、std::future等,可以实现如下一个符合要求的线程池,基本满足日常要求。

#include <functional>
#include <thread>
#include <condition_variable>
#include <future>
#include <atomic>
#include <queue>
#include <vector>

class threadpool
{
    using Task = std::function<void()>;

public:
    threadpool(size_t size = 4)
        : _stop(false)
    {
        size = size < 1 ? 1 : size;

        for (size_t i = 0; i < size; ++i)
        {
            _pool.emplace_back(&threadpool::schedual, this);
        }
    }

    ~threadpool()
    {
        shutdown();
    }

    // 提交一个任务
    template<class F, class... Args>
    auto commit(F&& f, Args&&... args) -> std::future<decltype(f(args...))>
    {
        using ResType = decltype(f(args...));// 函数f的返回值类型

        auto task = std::make_shared<std::packaged_task<ResType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        {
            // 添加任务到队列
            std::lock_guard<std::mutex> lock(_taskMutex);

            _tasks.emplace([task]()
            {
                (*task)();
            });
        }

        _taskCV.notify_all(); //唤醒线程执行

        std::future<ResType> future = task->get_future();
        return future;
    }

private:
    // 获取一个待执行的task
    Task get_one_task()
    {
        std::unique_lock<std::mutex> lock(_taskMutex);

        _taskCV.wait(lock, [this]() { return !_tasks.empty() || _stop.load(); }); // wait直到有task

        if (_stop.load())
        {
            return nullptr;
        }

        Task task{ std::move(_tasks.front()) }; // 取一个task
        _tasks.pop();
        return task;
    }

    // 任务调度
    void schedual()
    {
        while (!_stop.load())
        {
            if (Task task = get_one_task())
            {
                task();
            }
        }
    }

    // 关闭线程池,并等待结束
    void shutdown()
    {
    	std::unique_lock<std::mutex> lock(_taskMutex);
        
        this->_stop.store(true);
        _taskCV.notify_all();

        for (std::thread &thrd : _pool)
        {
            thrd.join();// 等待任务结束, 前提:线程一定会执行完
        }
    }

private:
    // 线程池
    std::vector<std::thread> _pool;

    // 任务队列
    std::queue<Task> _tasks;

    // 同步
    std::mutex _taskMutex;
    std::condition_variable _taskCV;

    // 是否关闭提交
    std::atomic<bool> _stop;
};

测试demo

void func1()
{
    std::cout << "hello, f !" << std::endl;
}

struct struct1
{
    int operator()()
    {
        std::cout << "hello, g !" << std::endl;
        return 42;
    }
};

class class1
{
public:
    double class_func(double in)
    {
        return in;
    }
};

int main(int argc, char *argv[])
{
    threadpool executor(10);

    std::future<void> ret_func1 = executor.commit(func1);
    std::future<int> ret_struct1 = executor.commit(struct1{});
    std::future<std::string> ret_lambda1 = executor.commit([]()->std::string { std::cout << "hello, h !" << std::endl; return "hello,fh !"; });

    class1 class1_;
    std::future<double> ret_float1 = executor.commit(std::bind(&class1::class_func, &class1_, 0.9));

    std::cout << "ret_struct1:" << ret_struct1.get()
        << "\nret_lambda1:" << ret_lambda1.get()
        << "\nret_float1:" << ret_float1.get() << std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(1));

    std::future<int> ret_struct2 = executor.commit(struct1{});
    int ret_struct2_int = ret_struct2.get();

    std::cout << "end..." << std::endl;

    return 0;
}

参考

c++11线程池实现