C++11线程池简洁实现

C++11标准里新增了多线程库,可以有效简化多线程代码的编写。下面的代码是基于C++11的线程池实现,原理也很简单,类似于生产者消费者,即有n个线程,相当于消费者,还有一个任务队列,相当于生产者。当任务队列被塞入任务时,线程们就去竞争这些任务,但每次只有一个线程能够得到任务。

下面来说说具体的实现,首先任务的类型就是一个没有参数,返回值为void的函数,使用using Task = std::function;声明了。当然我们也可以调用有参数的函数,通过std::bind绑定参数即可。

我们创建了一个线程池类ThreadPool,先来看构造函数,根据传入的数量,创建对应数量的线程,注意:线程一旦创建就开始运行了。这里使用_work_thread.emplace_back(std::bind(&ThreadPool::Worker, this));来向线程池中投入线程,emplace_back()是vector在C++11中的新方法,意思是使用括号里面的参数来创建对象,作为vector的成员。这里我们使用std::bind(&ThreadPool::Worker, this)来创建线程对象,也就是新创建的线程里面运行这个函数。

接着来看添加任务的函数Append,因为任务队列是所有线程共享的,所以需要使用一个互斥锁保护起来,只有取得锁之后才能进行操作。这里我们使用如下代码,在一个作用域里面使用mutex去初始化unique_lock对象,这是一种良好的编程技法,unique_lock使用RAII机制,它可以在初始化时对mutex加锁,退出作用域的时候自动对mutex进行解锁,确保了不会发生忘记解锁的尴尬。另外我们对于Task,使用右值引用来优化,可以避免不必要的拷贝。最后,当往任务队列成功插入任务后,我们使用_cond.notify_one();去通知任意一个线程。

void ThreadPool::Append(Task&& task)
{
    {
        std::unique_lock lock(_mutex);
        _task_queue.push_back(std::move(task));
    }
    _cond.notify_one();
}

现在来看工作线程,如下,工作线程其实是一个循环,每一次循环都去竞争,想要获得任务队列的使用权。这里的竞争使用了互斥锁和条件变量,具体的使用请搜索“C++11 条件变量”,简单来讲就是互斥锁和条件变量要配合使用,让所有的工作线程都阻塞在_cond.wait(lock);这里,当主线程往任务队列插入任务并notify_one,在众多的工作线程里面就会有一个天选之子被唤醒,从任务队列中取出任务执行,而其他没有被选中的线程就继续沉睡。这里为了降低占用锁的时间,使用了swap,创建一个新的空的任务队列,直接与旧的队列交换,交换只是缓冲区指针简单的互换,这样可以让锁的粒度大大减小。

void ThreadPool::Worker()
{
    while(!quited)
    {
        std::vector tasks;
        {
            std::unique_lock lock(_mutex);
            _cond.wait(lock);
            tasks.swap(_task_queue);
        }
        for (int i = 0; i < tasks.size(); ++i)
        {
            tasks[i]();
        }
    }
}

最后是析构函数,首先把quited置为True,再通知所有线程,这样所有线程就都被唤醒,退出while循环,也就结束了生命。最后再一个for循环等待工作线程的终止,回收资源。

下面是完整源代码:
threadpool.cpp

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

class ThreadPool
{
public:
    using Task = std::function;

    ThreadPool(int num);
    ~ThreadPool();
    void Append(Task&& task);

private:
    void Worker();

    std::mutex _mutex;
    std::condition_variable _cond;
    std::vector _work_thread;
    std::vector _task_queue;

    bool quited;
};

ThreadPool::ThreadPool(int num) : quited(false)
{
    assert(num > 0);
    for (int i = 0; i < num; ++i)
    {
        _work_thread.emplace_back(std::bind(&ThreadPool::Worker, this));
    }
}

ThreadPool::~ThreadPool()
{
    {
        std::unique_lock lock(_mutex);
        quited = true;
    }
    _cond.notify_all();
    for (auto& t : _work_thread)
    {
        t.join();
    }
}

void ThreadPool::Append(Task&& task)
{
    {
        std::unique_lock lock(_mutex);
        _task_queue.push_back(std::move(task));
    }
    _cond.notify_one();
}

void ThreadPool::Worker()
{
    std::cout << "Create Worker Thread: " << std::this_thread::get_id() << std::endl;
    while(!quited)
    {
        std::vector tasks;
        {
            std::unique_lock lock(_mutex);
            _cond.wait(lock);
            tasks.swap(_task_queue);
        }
        for (int i = 0; i < tasks.size(); ++i)
        {
            tasks[i]();
        }
    }
    std::cout << "Quit from Worker Thread: " << std::this_thread::get_id() << std::endl;
}

// 测试
void fun()
{
    std::cout << "Work in thread: " << std::this_thread::get_id() << std::endl;
}

int main()
{
    ThreadPool tp(4);
    sleep(1);
    for (int i = 0; i < 10; ++i)
    {
        tp.Append(fun);
        sleep(1);
    }
}

在linux下使用g++编译:g++ -std=c++11 threadpool.cpp -o threadpool -lpthread
执行生成的threadpool:./threadpool
运行结果:
C++11线程池简洁实现_第1张图片

你可能感兴趣的:(C++11线程池简洁实现)