C++11 线程池

来源:互联网 发布:天刀女性捏脸数据下载 编辑:程序博客网 时间:2024/05/29 06:59

注意:由于内部采用的deque,不是线程安全的。线程安全版本在 http://blog.csdn.net/p2016/article/details/77857324

C++11里多线程的库,可以看这里http://www.cplusplus.com/reference/multithreading/

线程池的话,主要包含线程池管理器,工作线程,任务借口, 任务队列,

首先,我用互斥量mutex + 条件变量condition_variable 实现了一个信号量semaphore。

//semaphore.h#pragma once#include<mutex>#include<condition_variable>class semaphore{private:int count;std::mutex m_mutex;std::condition_variable cv;public:semaphore(int c=1) :count(c){}void wait(){std::unique_lock<std::mutex> lck(m_mutex);if (--count < 0)cv.wait(lck);}void signal(){std::lock_guard<std::mutex> lg(m_mutex);if(++count<=0)cv.notify_one();}};
thread_instance.h是对thread的包装,有begin,set_task,join等借口。

//thread_instance.h#pragma once#include<functional>#include<condition_variable>#include<atomic>#include"semaphore.h"class thread_pool;class thread_instance{private:std::atomic<bool> stop;thread_pool * pool;std::mutex m_mutex;std::thread* pthread;std::function<void(void)> task;public:semaphore sem_task_ready;//std::condition_variable condi_task_ready;public:thread_instance(thread_pool *threadpool);~thread_instance();void begin();void run();void set_task(std::function<void(void)>);void join();};
thread_instance.cpp

#include "thread_instance.h"#include "thread_pool.h"thread_instance::thread_instance(thread_pool *threadpool) :stop(false), pool(threadpool), sem_task_ready(0){}thread_instance::~thread_instance(){if (pthread != nullptr){pthread->join();delete pthread;}}void thread_instance::begin(){pthread = new std::thread(std::bind(&thread_instance::run, this));}void thread_instance::run(){while (true){sem_task_ready.wait();//注意顺序,否则死锁。std::unique_lock<std::mutex> lg(m_mutex);//condi_task_ready.wait(lg);if (stop)break;task();//std::cout << this << std::endl;pool->append_free_thread(this);}}void thread_instance::set_task(std::function<void(void)> m_task){std::lock_guard<std::mutex> lg(m_mutex);task = m_task;sem_task_ready.signal();}void thread_instance::join(){stop = true;std::lock_guard<std::mutex> lg(m_mutex);task = nullptr;sem_task_ready.signal();}

线程池thread_pool,有start,append_task,join_all等接口。

//thread_pool.h#pragma once#include<atomic>#include<thread>#include<condition_variable>#include<mutex>#include<vector>#include<list>#include<queue>#include<functional>#include"semaphore.h"class thread_instance;class thread_pool{private:std::atomic<bool> stop;int count;//std::mutex mutex_task;std::mutex mutex_thread;std::mutex m_mutex;semaphore sem_free_thread;semaphore sem_task;std::vector<thread_instance*> vec_thread;std::queue<thread_instance*> list_free_thread;std::list<std::function<void(void)>> list_task;public:thread_pool(int c);~thread_pool();void start();void start_thread();void join_all();void append_task(std::function<void(void)> func);void append_free_thread(thread_instance* pthread);};

thread_pool.cpp

#include "thread_pool.h"#include"thread_instance.h"thread_pool::thread_pool(int c) :stop(false), count(c), sem_free_thread(c), sem_task(0){for (int i = 0; i < c; i++){thread_instance* p = new thread_instance(this);vec_thread.push_back(p);list_free_thread.push(p);}}thread_pool::~thread_pool(){for (auto x : vec_thread){delete x;}}void thread_pool::start(){std::thread t(std::bind(&thread_pool::start_thread, this));t.detach();}void thread_pool::start_thread(){for (auto free_thread : vec_thread)free_thread->begin();while (true){sem_free_thread.wait();sem_task.wait();thread_instance *ptr = list_free_thread.front();list_free_thread.pop();ptr->set_task(list_task.front());list_task.pop_front();}}void thread_pool::join_all(){for (auto x : vec_thread)x->join();stop = false;sem_free_thread.signal();sem_task.signal();}void thread_pool::append_task(std::function<void(void)> func){//std::lock_guard<std::mutex> lg(m_mutex);//no need.list_task.push_back(func);sem_task.signal();}void thread_pool::append_free_thread(thread_instance *pthread){std::lock_guard<std::mutex> lg(m_mutex);//necessary, 34行。list_free_thread.push(pthread);sem_free_thread.signal();}

测试

#include <iostream>#include <thread>#include <mutex>#include <functional>#include <list>#include <atomic>#include <vector>#include <algorithm>#include <memory>#include <condition_variable>#include"thread_pool.h"using namespace std;mutex m_mut;class A{public:A() {}~A() {}public:void foo(int k){//lock_guard<mutex> lg(m_mut);//sleep for a whilestd::this_thread::sleep_for(std::chrono::milliseconds(rand() % 900 + 100));std::cout << "k = " << k << std::endl;}};//a function which will be executed in sub thread.void hello(){lock_guard<mutex> lg(m_mut);//sleep for a whilestd::this_thread::sleep_for(std::chrono::milliseconds(100));cout << "hello  \n";}//let's test the thread.int main(){srand(0);thread_pool g_threadPool(3);A a;g_threadPool.append_task(&hello);//append object method with copy-constructor(value-assignment)    g_threadPool.append_task(std::bind(&A::foo, a, 1));g_threadPool.append_task(std::bind(&A::foo, a, 2));g_threadPool.append_task(std::bind(&A::foo, a, 3));g_threadPool.append_task(std::bind(&A::foo, a, 4));g_threadPool.start();g_threadPool.append_task(&hello);//append object method with address assignment, will cause the objects' member increase.g_threadPool.append_task(std::bind(&A::foo, &a, 5));g_threadPool.append_task(std::bind(&A::foo, &a, 6));g_threadPool.append_task(std::bind(&A::foo, &a, 7));g_threadPool.append_task(std::bind(&A::foo, &a, 8));//std::this_thread::sleep_for(std::chrono::seconds(5));char temp;cin >> temp;if (temp == 'e'){g_threadPool.join_all();}//auto end = std::chrono::high_resolution_clock().now();//auto dd = std::chrono::duration_cast<chrono::seconds>(end - beg);//cout << dd.count() << endl;return 0;}

说说这个线程池的流程吧。start启动线程池并初始化,创建一个线程来执行start_thread并detch来防止阻塞主线程,start_thread使线程队列都处在等待任务的阻塞状态。执行循环,利用信号量判断是否有空闲线程和任务,然后使空闲线程设置任务,并通知他自己run。。执行完后把线程再还给空闲线程list。(thread_instance中的set_task()和run()就好像生产者消费者问题,传输的元素就是task。)


说说自己遇到的几个问题吧。

1,本来thread_instance中为了实现没有任务时阻塞,采用的是条件变量,测试了之后有时候一些任务线程没有执行?后来使用了信号量,解决了这个问题。猜测是应为当条件变量notice时,没有wait的地方,直接被忽略了(其实是虚假唤醒,在wait地方加入while循环就行了,这样notic后还要再检验一下task)。信号量也可以解决这个问题。

2,原来当执行到中间某个任务时,很大的概率会中断,debug发现中断在thread_pool.cpp第35行,thread_instance *ptr = list_free_thread.front();,debug信息是front迭代器dereferenceable。这时发现front元素的pthread是0xcdcdcdcd,百度之后发现0xcdcdcdcd代表的是堆上的数据未初始化。猜测可能是因为List不是线程安全的,它的push操作在多线程下是不安全的,可能是appen_free_thread()。后来把void thread_pool::append_free_thread(thread_instance *pthread)这个函数里边加锁,但是还解决这个问题。后来把list换做queue,解决了。




一些C++11新特性,

function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类 的非静态成员函数时。

std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别),如果要绑定到类的非静态成员函数,则需要使用std::bind。

他们的实现就是一个重载了()运算符的模版类。




原创粉丝点击