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. 快递100API接口调用代码示例

    package com.util; import java.io.IOException; import java.io.InputStream; import java.net.MalformedU ...

  2. [原][osgearth]earth文件加载道路一初步看见模型道路

    时间是2017年2月5日17:16:32 由于OE2.9还没有发布,但是我又急于使用OE的道路. 所以,我先编译了正在github上调试中的OE2.9 github网址是:https://github ...

  3. 使用Bootstrap插件datapicker获取时间

    引入css和js <link rel="stylesheet" href="/${appName}/commons/css/datapicker/datepicke ...

  4. Leetcode 39

    //经典回溯法class Solution { public: vector<vector<int>> combinationSum(vector<int>& ...

  5. IOS-更优雅地使用Static Cell

    更优雅地使用Static Cell 在项目开发中,经常会用到static cell来实现一些固定的列表界面(如:个人中心等),在static cell被点击时,如何判断被点击的cell是哪一个,有什么 ...

  6. Tomcat : IOException while loading persisted sessions: java.io.EOFException

    严重: IOException while loading persisted sessions: java.io.EOFException严重: Exception loading sessions ...

  7. redis安装全过程

    1. 从官网上下载redis. 2.安装gcc 3.进入./redis/src目录下make MALLOC =libc 4.遇到的问题 Redis简介: Redis是一个开源的使用ANSI C语言编写 ...

  8. springboot结合开源editor.md集成markdonw编辑器

    今天来实现一个简单的功能,通常blog后台编辑大多使用的是富文本编辑器,比如百度的Ueditor,比较轻巧的wangEditor,那么如何使用开源editor.md的markdown呢? 搭建一个sp ...

  9. animationx详解

    animation是CSS3中极其强大的功能,它可以完成许多炫酷有趣的动画效果,网上也有非常不错的类库.下面将做详细介绍. 1.@keyframes:用于定义动画的具体动作(帧动作),一般要加上浏览器 ...

  10. bootstrap 在超小屏布局时使用 clearfix

    bootstrap 在超小屏布局时使用 clearfix 先看案例,一共四个 div,使用 col-xs-6, 所以在特别小型设备上时会变成两行. 不过我们发现如果第一个 div 内容多了后会变成如下 ...