分享一个C++11写的线程池

来源:互联网 发布:上海瀚威酩轩 知乎 编辑:程序博客网 时间:2024/06/10 08:11

上一篇博客讲述了一些线程池的知识。

但是C++11让线程变得更加简单,关于C++11线程方面的知识之前也有过介绍。

今天就介绍一个使用C++11写的简单的一个线程池,thread pool.

github地址:
https://github.com/progschj/ThreadPool

首先看看线程池怎么写的:

#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);    template<class F, class... Args>    auto enqueue(F&& f, Args&&... args)         -> std::future<typename std::result_of<F(Args...)>::type>;    ~ThreadPool();private:    // need to keep track of threads so we can join them    std::vector< std::thread > workers;    // the task queue    std::queue< std::function<void()> > tasks;    // synchronization    std::mutex queue_mutex;    std::condition_variable condition;    bool stop;};// the constructor just launches some amount of workersinline 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();                }            }        );}// add new work item to the pooltemplate<class F, class... 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);        // don't allow enqueueing after stopping the pool        if(stop)            throw std::runtime_error("enqueue on stopped ThreadPool");        tasks.emplace([task](){ (*task)(); });    }    condition.notify_one();    return res;}// the destructor joins all threadsinline ThreadPool::~ThreadPool(){    {        std::unique_lock<std::mutex> lock(queue_mutex);        stop = true;    }    condition.notify_all();    for(std::thread &worker: workers)        worker.join();}#endif

下面是简单的调用:

#include <iostream>#include <vector>#include <chrono>#include "ThreadPool.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;}

对上面的源码进行简单的剖析:

condition_variable头文件
头文件主要包含了与条件变量相关的类和函数。相关的类包括 std::condition_variable 和 std::condition_variable_any,还有枚举类型std::cv_status
可以参考:http://www.cnblogs.com/haippy/p/3252041.html

future头文件
前面已经多次提到过 std::future,那么 std::future 究竟是什么呢?简单地说,std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)。

一个有效(valid)的 std::future 对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,其实我们前面都已经提到了,他们分别是:

参考:http://www.cnblogs.com/haippy/p/3280643.html

stdexcept头文件
定义了一些标准的异常类。分为两大类:逻辑错误和运行时错误。其中运行时错误是程序员不能控制的。

逻辑错误都继承自 logic_error

domain_error 域错误

invalid_argument 非法参数

length_error 通常是创建对象是给出的尺寸太大

out_of_range 访问超界

运行时错误都继承自runtime_error

overflow_error 上溢

range_error 超出表示范围

underflow_error 下溢

emplace_back和push_back的区别
emplace_back和push_back都是向容器内添加数据.
对于在容器中添加类的对象时, 相比于push_back,emplace_back可以避免额外类的复制和移动操作.

std::chrono
过定义这些常用的时间间隔类型,我们能方便的使用它们,比如线程的休眠:

std::this_thread::sleep_for(std::chrono::seconds(3)); //休眠三秒
std::this_thread::sleep_for(std::chrono:: milliseconds (100)); //休眠100毫秒

基于范围的for循环
基于范围的for循环是老生常谈了:
for(auto result:results)

如果要改变results的内容,则可使用引用:
for(auto & result:results)

那么这个是什么意思呢?
for(auto && result:results)

即:I will accept any initializer regardless of whether it is an lvalue or rvalue expression and I will preserve its constness

using定义别名

template<typename T>using Tlist = std::list<T>;

C++中构造函数和析构函数可以是内联函数吗
首先明确,这不是C++11新内容
无论是构造函数还是析构函数都可以声明为inline的:

class Foo {    int* p;public:    Foo();    ~Foo();};inline Foo::Foo() {     p = new char[0x00100000]; }inline Foo::~Foo(){     delete [] p; }

需要明确是:
Defining the body of the constructor INSIDE the class has the same effect of placing the function OUTSIDE the class with the “inline” keyword.
In both cases it’s a hint to the compiler. An “inline” function doesn’t necessarily mean the function will be inlined. That depends on the complexity of the function and other rules.

lambda表达式
此处略去一千字

可变参数模板
此处略去一万字

1 0
原创粉丝点击