1.C++11中引入了lambada表达式,很好的支持异步编程

2.C++11中引入了std::thread,可以很方便的构建线程,更方便的可移植特性

3.C++11中引入了std::mutex,可以很方便的构建线程锁互斥访问,更方便的可移植特性

4.C++11中引入了std::condition_variable,可以不依赖于win32 api实现自己的消费者生产者模型

5.利用改进版本的shared_ptr,可以很好的解决多线程生命周期的棘手问题

 /************************************************************************/
/* */
/************************************************************************/ #ifndef __CARBON_THREAD_POOL_H
#define __CARBON_THREAD_POOL_H #include <vector>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
#include <string>
#include <sstream>
#include <deque> namespace CARBON { //************************************
// Method: Create
// Returns: std::shared_ptr
// Qualifier: 用于创建智能指针实例
// Parameter: args, 可变参数,接受任意个数的参数,传递给T的构造函数
//************************************
template<typename T, typename... ARG>
std::shared_ptr<T> Create(ARG&&... args)
{
struct TEnableShared : public T
{
TEnableShared(ARG&&... args)
: T(std::forward<ARG>(args)...)
{}
}; return std::make_shared<TEnableShared>(std::forward<ARG>(args)...);
} class ThreadPool : public std::enable_shared_from_this<ThreadPool>
{
protected:
ThreadPool()
: _stop(false)
{} virtual ~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(_lock);
_stop = true;
}
_condition.notify_all();
for (std::thread &worker : _workers)
worker.join();
} public:
// initialize thread pool with number of threads
bool InitializePool(size_t threads)
{
if (!_workers.empty()) return true; for (size_t i = ; i < threads; ++i)
{
std::weak_ptr<ThreadPool> _wtp = this->shared_from_this();
auto th = [](std::weak_ptr<ThreadPool> wtp) {
for (;;)
{
std::function<void()> task; {
std::shared_ptr<ThreadPool> stp = wtp.lock();
if (!stp)
return; std::unique_lock<std::mutex> lock(stp->_lock);
auto shipment = [&] ()->bool { return stp->_stop || !stp->_tasks.empty(); };
stp->_condition.wait(lock, shipment);
if (stp->_stop)
return;
if (stp->_tasks.empty())
continue;
task = std::move(stp->_tasks.front()).task;
stp->_tasks.pop_front();
} task();
}
};
_workers.emplace_back(th, _wtp);
} return !_workers.empty();
} //************************************
// Method: EnqueueTask
// Returns: std::future, 值类型由functor f指定
// Qualifier: 可以借由返回的std::future获取结果,但是更建议在functor中做异步通知
// Parameter: taskid 用于接受任务的id描述
// Parameter: functor f, 函数对象,用于执行任务
// Parameter: args, 可变参数,接受任意个数的参数,传递给functor f
//************************************
template<class F, class... Args>
auto EnqueueTask(std::string& taskid, F&& f, Args&&... args)
->std::future<typename std::result_of<F(Args...)>::type>
{
if (_workers.empty())
throw std::runtime_error("ThreadPool not initialized yet"); 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(_lock); // don't allow enqueueing after stopping the pool
if (_stop)
throw std::runtime_error("enqueue on stopped ThreadPool"); stThreadTask st;
std::stringstream ss;
ss << (void*)task.get();
ss >> taskid;
st.taskid = taskid;
st.task = [task]() { (*task)(); };
_tasks.push_back(st);
}
_condition.notify_one();
return res;
} //************************************
// Method: GetTasksSize
// Returns: size_t
// Qualifier: 获取等待任务队列的任务数,正在执行的任务已经弹出队列,所以不参与计算
//************************************
size_t GetTasksSize()
{
std::unique_lock<std::mutex> lock(_lock);
return _tasks.size();
} //************************************
// Method: RemoveTask
// Returns: bool, 找到任务并移除则返回true,否则返回false
// Qualifier: 正在执行的任务已经弹出任务队列,应该在其它地方通知任务退出
// Qualifier: 执行完成的任务已经弹出任务队列,无法移除不存在的任务
// Qualifier: 该接口只能移除处在等待中的任务
// Parameter: taskid是任务的唯一标示,由EnqueueTask返回
//************************************
bool RemoveTask(const std::string& taskid)
{
std::unique_lock<std::mutex> lock(_lock);
for (auto& t = _tasks.begin(); t != _tasks.end(); ++t)
{
if (taskid == t->taskid)
{
_tasks.erase(t);
return true;
}
} return false;
} private:
typedef struct stThreadTask
{
std::function<void()> task;
std::string taskid;
}stThreadTask; // need to keep track of threads so we can join them
std::vector< std::thread > _workers;
// the task queue
std::deque< stThreadTask > _tasks; // synchronization
std::mutex _lock;
std::condition_variable _condition;
bool _stop;
};
} #endif

使用enable_shared_from_this来确保内部线程访问指针时,不会因为指针失效造成的非法访问

weak_ptr很好的保证了ThreadPool的生命周期安全性和实效性

由于使用了share_from_this,将初始化代码整体拿出来放到InitializePool中实现

ThreadPool的构造函数和析构函数声明为protected,用于保证外部不要直接生成ThreadPool实例

应该使用Create函数来生成ThreadPool实例

测试代码如下:

 namespace {
std::condition_variable _exit_cv;
} void func(int n)
{
std::cout << "func with n " << n << std::endl;
} using CARBON::ThreadPool; std::string taskid;
std::shared_ptr<ThreadPool> stp = CARBON::Create<ThreadPool>();
std::weak_ptr<ThreadPool> _wtp = stp;
stp->InitializePool(); stp->EnqueueTask(taskid, [](std::function<void(int)> cbf, std::weak_ptr<ThreadPool> wtp) ->int {
std::cout << "task1\n"; for (int i = ; i < ; ++i) {
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
if(_exit_cv.wait_for(lck, std::chrono::milliseconds()) == std::cv_status::no_timeout)
break; if (cbf) cbf(i);
if (wtp.expired())
break;
} return ;
}, func, _wtp);

当需要中断线程执行时,应该在外部通知线程中的任务自行退出

例子中可以在主线程中这么做

    _exit_cv.notify_all();
_exit_cv用于模拟sleep操作
func用于模拟任务结果的异步通知,这里为了省事使用了函数指针,实际工作中应该使用functor来传递,以保证生命周期的有效性
比如std::bind和shared_ptr一起构造的functor对象

基于C++11实现的线程池的更多相关文章

  1. 基于无锁队列和c++11的高性能线程池

    基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上   标签: <无>   代码片段(6)[ ...

  2. [C++]C风格、C++风格和C++11特性的线程池

    线程池概念 假设完成一项任务需要的时间=创建线程时间T1+线程执行任务时间T2+销毁线程时间T3,如果T1+T3的时间远大于T2,通常就可以考虑采取线程池来提高服务器的性能 thread pool就是 ...

  3. C++11的简单线程池代码阅读

    这是一个简单的C++11实现的线程池,代码很简单. 原理就是管理一个任务队列和一个工作线程队列. 工作线程不断的从任务队列取任务,然后执行.如果没有任务就等待新任务的到来.添加新任务的时候先添加到任务 ...

  4. 第11章 Windows线程池(3)_私有的线程池

    11.3 私有的线程池 11.3.1 创建和销毁私有的线程池 (1)进程默认线程池 当调用CreateThreadpoolwork.CreateThreadpoolTimer.CreateThread ...

  5. 第11章 Windows线程池(1)_传统的Windows线程池

    第11章 Windows线程池 11.1 传统的Windows线程池及API (1)线程池中的几种底层线程 ①可变数量的长任务线程:WT_EXECUTELONGFUNCTION ②Timer线程:调用 ...

  6. Windows核心编程:第11章 Windows线程池

    Github https://github.com/gongluck/Windows-Core-Program.git //第11章 Windows线程池.cpp: 定义应用程序的入口点. // #i ...

  7. 基于队列queue实现的线程池

    本文通过文章同步功能推送至博客园,显示排版可能会有所错误,请见谅! 写在前文:在Python中给多进程提供了进程池类,对于线程,Python2并没有直接提供线程池类(Python3中提供了线程池功能) ...

  8. 第11章 Windows线程池(2)_Win2008及以上的新线程池

    11.2 Win2008以上的新线程池 (1)传统线程池的优缺点: ①传统Windows线程池调用简单,使用方便(有时只需调用一个API即可) ②这种简单也带来负面问题,如接口过于简单,无法更多去控制 ...

  9. 使用C++11 实现的线程池

    最近打算做一个服务器端程序,每来一个客户端请求新开一个线程进行处理.在网上查了一些资料后,准备使用线程池来做这个东西.使用C++11新的库处理想线程问题比以前简单了许多,在网上找到一份线程池的实现,h ...

随机推荐

  1. JQuery中width和JS中JS中关于clientWidth offsetWidth scrollWidth 等的含义

    JQuery中: width()方法用于获得元素宽度: innerWidth()方法用于获得包括内边界(padding)的元素宽度: outerWidth()方法用于获得包括内边界(padding)和 ...

  2. Linux删除(清空)正在运行的应用日志文件内容 及 查看服务器剩余空间

    在测试环境定位问题时,如果发现日志文件内容太多或太大,有时需要删除该日志,如Tomcat,Nginx日志.以前每次都是先rm -rf ***.log,然后重启应用.直到后来发现了以下命令,原来可以不用 ...

  3. R语言画图小结

    本文以1950年到2010年期间我国的火灾统计数据为例,数据如下所示: (0)加载数据 data<-read.csv("E:\\MyDocument\\p\\Data\\1950~20 ...

  4. idea 设置编译快捷键(代替 ctrl+f9)

    问题描述 今日在设置项目热部署的时候,无奈就是不会自动编译,不知什么原因. 而编译的话,要么去点小按钮 ,要么使用ctrl + f9,实在不便. 且ctrl + f9目测不能更改. 解决办法 借鉴了关 ...

  5. Tornado源码分析 --- Redirect重定向

    “重定向”简单介绍: “重定向”指的是HTTP重定向,是HTTP协议的一种机制.当client向server发送一个请求,要求获取一个资源时,在server接收到这个请求后发现请求的这个资源实际存放在 ...

  6. opencv错误(Unhandled expection at at 0x0007EEE...)

    错误截图如下: 情况1:加载人脸检测分类器的时候出错,不能写相对路径一定要写绝对路径 例如:cascade.load("D:\\recognise-your-own-face2\\recog ...

  7. WEB标准以及W3C的理解和认识

    web标准简单来说可以分为结构.表现和行为.其中结构主要是有HTML标签组成.表现即指css样式表,通过css可以是页面的结构标签更具美感.行为是指页面和用户具有一定的交互,同时页面结构或者表现发生变 ...

  8. SQL语法语句总结(《SQL必知必会》读书笔记)

    一.SQL语句语法 ALTER TABLE ALTER TABLE 用来更新已存在表的结构. ALTER TABLE tablename (ADD|DROP column datatype [NULL ...

  9. 如何在Oracle官网下载java的JDK最新版本和历史版本

    官网上最显眼位置只显示了Java SE的JDK的最新版本下载链接,因为都是英文,如果英文不是很好,寻找之前的JDK版本需要很长时间,而且未必能在那个隐蔽的位置找到之前版本列表. 今天小编来给你详细讲解 ...

  10. 在Mac上安装anaconda,在命令行中输入conda,提示不是有效命令的解决办法

    原链接:https://stackoverflow.com/questions/18675907/how-to-run-conda