C++11 线程池

代码如下

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

constexpr const size_t DEFAULT_CAPACITY = 1;

class thread_pool {
    typedef std::function task;

    std::mutex _m;
    std::condition_variable _cv;
    std::atomic _stop;
    std::vector _work_threads;
    std::queue _vector;
public:
    explicit thread_pool(size_t cap = DEFAULT_CAPACITY) : _vector{}, _work_threads{}, _m{}, _cv{}, _stop{false} {
        for (size_t i = 0; i < cap; i++) {
            _work_threads.emplace_back(&thread_pool::run, this);
        }
    }

    thread_pool(const thread_pool &) = delete;

    ~thread_pool() {
        _stop = true;
        _cv.notify_all();
        for (size_t i = 0; i < DEFAULT_CAPACITY; i++) {
            if (_work_threads[i].joinable()) {
                _work_threads[i].join();
            }
        }
        while (!_vector.empty()) {
            task task_ins = _vector.front();
            task_ins();
            _vector.pop();
        }
    }

    template
    auto emplace(Ft &&ft, Args &&... args) -> std::future()(std::declval()...))> {
        typedef decltype(std::declval()(std::declval()...)) Rt;
        std::shared_ptr> promise = std::make_shared>();
        auto future = promise->get_future();
        {
            std::lock_guard lk(_m);
            _vector.push([promise, &ft, &args...]() {
                auto result = ft(args...);
                promise->set_value(result);
            });
        }
        _cv.notify_one();
        return future;
    }

    void emplace(std::function &&task) {
        std::lock_guard lk(_m);
        _vector.push(task);
    }

    void emplace(void(*task)()) {
        std::lock_guard lk(_m);
        _vector.push(task);
    }


    void run() {
        while (!_stop) {
            std::unique_lock lk(_m);
            _cv.wait(lk, [&] { return !_vector.empty() || _stop == true; });
            if (_vector.empty()) {
                continue;
            }
            auto task_ins = _vector.front();
            _vector.pop();
            lk.unlock();
            task_ins();
        }
        std::cout << "finish" << std::endl;
    }
};


class Foo {
public:
    Foo() = delete;

    explicit Foo(int v) : value(v) {}

    int operator()() {
        return value;
    }

private:
    int value;
};


void global_func() {
    std::cout << "global function called" << std::endl;
};

void test() {
    thread_pool pool{};
    auto result1 = pool.emplace(Foo{1});
    std::cout << "value = " << result1.get() << std::endl;
    pool.emplace(Foo{1});
    pool.emplace(Foo{1});
    pool.emplace(Foo{1});
    pool.emplace(global_func);
    pool.emplace(std::function{[] {
        std::cout << "lambda called" << std::endl;
    }});
}

int main() {
    test();
    std::cout << "清理完成" << std::endl;
    return 0;
}

发现的问题

  • 虽然利用RAII机制确保线程池析构的时候可以完成所有task,但是最终执行时,是在执行线程池析构函数的线程中统一执行的,这样可能带来性能损耗。最好的办法是析构线程之前处理完所有任务,为此当_stop为true时,工作线程统一执行clean函数,在clean函数中统一执行所有任务。
  • 异常处理机制缺少,如果用户提交的task中包含未捕获的异常将会导致工作线程异常退出。应该在emplace中添加try/catch 并且统一调用promise->setException 交由其他线程处理。
  • join机制,使得当工作线程出现致命异常时,主线程也会terminate。最好的办法是将pool detch掉,确保在工作线程出现致命异常时不会连累主线程。但是这需要一个更好的通讯机制可以确保通知work_thread 线程池被析构了。并且使得工作线程早于线程池的析构。
  • emplace函数重载问题,由于使用了template函数,使得对于 void(…)类型的调用对象无法decltype出对应的return type。目前暂时的解决办法是声明不同的参数类型的函数,利用c++的函数匹配优先级暂时解决。最好的办法是利用type_trails人工判断,但是这就要求emplace必须为一个class template。
  • emplace 函数使用的是std::forward 当用户传递进来的参数是一个无效的左值引用时,会导致报错。可以模仿std::thread的做法添加decay_copy type_trailt 强制使用参数的拷贝构造函数创建新的参数。
  • std::future没有then机制,无法创建完美的链式调用,这个问题可以模仿folly的future。

总结一下

c++ 任重而道远,且行且珍惜(enumm 放弃。)

你可能感兴趣的:(C++)