boost之threadpool(提高之线程池)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
boost之threadpool(提高之线程池)
提高之threadpoolboost之线程池
线程池是基于升压库实现的一个线程池子库,但线程池实现起来不是很复杂。
我们从线程池中又能学到什么东西呢?
它是基于升压库实现的,如果大家对升压库有兴趣,看看一个简单的实现还是可以学到点东西的。
线程池基本功能
1、任务封装,包括普通任务(task_func)和优先级任务
(prio_task_func)。
2、调度策略,包括fifo_scheduler(先进先出)、lifo_scheduler (后进先出)、prio_scheduler(优先级)。
3、结束策略,包括wait_for_all_tasks(全部任务等待)、
wait_for_active_tasks(激活任务等待)、立即(立即结束)。
4、动态修改线程池个数功能。
5、基于未来封装的异步返回值获取功能。
在sorceforge上有一个用升压编写的线程池。
该线程池和升压结合的比较好,并且提供了多种任务执行策略,使用也非常简单。
下载地址:/这个线程池不需要编译,只要在项目中包含其头文件就可以了。
一、源代码分析
快速入门分析(/线程池线程池/快速/库/)
这个例子的代码很简单,但已经全部展示了线程池的核心内容,包括建立、调度、同步等操作。
查看plaincopy到clipboardprint?
//创建FIFO线程池容器用两个线程。
池TP(2);
/向池中添加一些任务。
TP。
时间表(&;first_task);
TP。
时间表(&;second_task);
/等待所有任务完成。
wait() TP;
//创建FIFO线程池容器用两个线程。
池TP(2);
/向池中添加一些任务。
TP。
时间表(&;first_task);
TP。
时间表(&;second_task);
/等待所有任务完成。
wait() TP;
池的定义具体见池。
HPP,但使用了pimpl模式,核心代码见pool_core HPP文件。
下面是池的定义
typedef thread_pool <;task_func,fifo_scheduler,
static_size,resize_controller,wait_for_all_tasks >;
fifo_pool;
typedef fifo_pool池;
从上面可以知道,池实际就是fifo_pool,从模板参数可以看到,使用了fifo_scheduler和wait_for_all_tasks。
对于线程池有点理解的都知道,一般都是那几样东西,线程的封装,条件变量,队列数据结构。
所以简单的能做的很简单,复杂的的就看你的策略需求了。
对基于升压库的线程池子库来说,上面的三样东西都是现成的,线程封装和条件变量直接使用螺纹子库就行,队列使用STL的标准容器。
task_adaptors.hpp
对线程任务的封装,所谓任务,我们可以理解成需要运行的函数。
线程池最大限度的使用了功能和绑定功能来封装函数,这点和螺纹子库类似。
文件中涉及的内容主要有三个:task_func、prio_task_func和looped_task_func。
对普通任务的封装
typedef void function0 <;>;task_func;
如果对绑定和功能熟悉的应该很好理解。
对优先级任务的封装
类prio_task_func
这个类很简单,重载了两个方法,
operator()是仿函数的用法,
运算符& lt;是用于优先级比较使用的,用于STL容器的元素比较。
size_policies.hpp
对大小的封装,包括empty_controller、resize_controller和static_size。
shutdown_policies.hpp
对线程池结束的策略封装,包括wait_for_all_tasks、
wait_for_active_tasks和立即。
这几个类很简单,具体操作封装在池中。
线程池运行过程中,包括队列中等待的任务,线程正在运行的任务。
所以结束的时候,对这些任务的策略操作是有选择的。
Scheduling_policies.hpp
Encapsulation of task scheduling testing, including
fifo_scheduler, lifo_scheduler, and prio_scheduler.
In fact, the similarity between the three classes is so high that you might prefer to implement them using inheritance and virtual functions.
As you mentioned earlier, the queue data structure for saving task looks very clear here.
FIFO and LIFO use std:: deque, prio uses std:: priority_queue, and other parts of the code have nothing to say.
Pool_adaptors.hpp
Several packages of global schedule functions.
Future.hpp
It seems that the thread child library also has future, but it's not clear whether it's the same thing.
ThreadPool's future is designed to encapsulate asynchronous function call return values.
A simple point of view is that when the schedule task, a pointer is bound between the two, and then you can get the return value through the future.
Of course, the process of getting the return value should be blocked and can only be wait when the task is incomplete.
Locking_ptr.hpp
LockingPtr simple package, specifically Google "volatile - Multithreaded Programmer's Best Friend".
ThreadPool uses the volatile keyword extensively, so LockingPtr protection is needed.
Scope_guard.hpp
Encapsulation of a function object calls a function object that is bound at the constructor when using the C++ destructor.
Worker_thread.hpp
Encapsulation of worker threads, this package does not refer to the underlying thread API package, as this part is provided by the thread child Library of boost.
Encapsulation refers to the logical function of executing task in a loop (threads run up to loop run, a function that gets task execution from the queue, and waits for it in idle time)
We focus on run and create_and_attach.
The two functions together look very clear, and
create_and_attach generates a thread by bind to execute the run method.
The statement in the run method is a simple loop operation,
(m_pool->) (while; execute_task) {}
So, when the execute_task returns false, the run function is over, and the bind of the function thread is over.
OK, here it is necessary to explain the whole call process simply.
Create FIFO thread pool container with / two threads.
Pool TP (2);
This operation calls the constructor of the pool
View, plaincopy, to, clipboardprint?
Thread_pool (size_t initial_threads = 0)
: m_core (new, pool_core_type)
M_shutdown_controller (static_cast< void*> (0); bind (& pool_core_type:: shutdown; m_core))
{
Size_policy_type:: init (*m_core, initial_threads);
}
Thread_pool (size_t initial_threads = 0)
: m_core (new, pool_core_type)
M_shutdown_controller (static_cast< void*> (0); bind (& pool_core_type:: shutdown; m_core))
{
Size_policy_type:: init (*m_core, initial_threads);
}
Because of the Pimpl schema, all of the code is encapsulated within m_core.
The default number of threads for pool is 0, initialized by size_policy_type:: init.
Size_policy_type is a template parameter, and pool corresponds to FIFO, so that's the static_size type.
The init function of the //static_size class
View, plaincopy, to, clipboardprint?
Static, void, init (Pool&, pool, size_t, const,
worker_count)
{
Pool.resize (worker_count);
}
Static, void, init (Pool&, pool, size_t, const,
worker_count)
{
Pool.resize (worker_count);
}
The resize function of //pool_core
This function is a bit long, mainly to do the dynamic configuration, the number of threads of the logical operation, and create_and_attach is also called here.
View, plaincopy, to, clipboardprint?
The create_and_attach function of //worker_thread
Static, void, create_and_attach (shared_ptr< pool_type> const; & pool)
{
Shared_ptr< worker_thread> worker (New worker_thread (pool));
If (worker)
{
//run is the loop function of the thread
Worker-> m_thread.
复位(新增加::线程(绑定(&;worker_thread::跑,工人)));
}
}
/ / worker_thread的create_and_attach函数
静态create_and_attach(shared_ptr <;pool_type >;const &;池)
{
shared_ptr <;worker_thread >;工人(新worker_thread(池));
如果(工人)
{
/ /运行是线程的环函数
工人- >;m_thread。
复位(新增加::线程(绑定(&;worker_thread::跑,工人)));
}
}
查看plaincopy到clipboardprint?
/ / worker_thread的运行函数
无效run()
{
scope_guard notify_exception(绑定(&;worker_thread::died_unexpectedly,这));
而(m_pool - >;execute_task()){ } /环直到返回值为假
notify_exception。
disable();
m_pool - >;worker_destructed(- & gt;shared_from_this());
}
/ / worker_thread的运行函数
无效run()
{
scope_guard notify_exception(绑定(&;worker_thread::died_unexpectedly,这));
而(m_pool - >;execute_task()){ } /环直到返回值为假
notify_exception。
disable();
m_pool - >;worker_destructed(- & gt;shared_from_this());
}
/ / pool_core的execute_task函数
这个函数有点长,简单点说,就是从队列中获取任务然后执行,如果队列为空,则线程需要等操作。
由于线程池支持动态调整线程个数,从该函数我们也是可以看出来是如何做到的。
查看plaincopy到clipboardprint?
/ /如有必要减少螺纹的数量
如果(m_worker_count >;m_target_worker_count)
{
返回false;/终止工
}
/ /如有必要减少螺纹的数量
如果(m_worker_count >;m_target_worker_count)
{
返回false;/终止工
}
池内部使用了多个整数来记录现在个数,譬如m_worker_count和m_target_worker_count。
m_worker_count是当前激活运行中的线程个数。
m_target_worker_count是最新动态配置的线程个数。
当个数不匹配时,通过返回假方式结束线程。
/向池中添加一些任务。
TP。
时间表(&;first_task);
查看plaincopy到clipboardprint?
/ / thread_pool的进度函数
布尔的时间表(task_type const &;任务)
{
返回m_core - >;进度(任务);
}
/ / pool_core的进度函数(和execute_task函数强相关)
布尔的时间表(task_type const &;任务)挥发
{
locking_ptr <;pool_type,recursive_mutex >;lockedthis (*,m_monitor);
如果(lockedthis - >;m_scheduler。
推(任务))
{
/ /任务成功入队列后,notify_one一个线程。
lockedthis - >;m_task_or_terminate_workers_event。
notify_one();
返回true;
}
其他的
返回false;
}
}
/ / thread_pool的进度函数
布尔的时间表(task_type const &;任务)
{
返回m_core - >;进度(任务);
}
/ / pool_core的进度函数(和execute_task函数强相关)
布尔的时间表(task_type const &;任务)挥发
{
locking_ptr <;pool_type,recursive_mutex >;lockedthis (*,m_monitor);
如果(lockedthis - >;m_scheduler。
推(任务))
{
/ /任务成功入队列后,notify_one一个线程。
lockedthis - >;m_task_or_terminate_workers_event。
notify_one();
返回true;
}
其他的
{
返回false;
}
}
/等待所有任务完成。
wait() TP;
/ / pool_core的等函数
虚空等(size_t const task_threshold = 0)const挥发性
bool等(xtime const &;时间戳,size_t const task_threshold = 0)const挥发性
等待函数是一个阻塞操作,内部逻辑实现使用了一个条件变量,提供
超时等待方式。
二、升压线程池使用实例
线程池可以减少创建和切换线程的额外开销,利用已经存在的线程多次循环执行多个任务从而提高系统的处理能力,有关线程池的概念可谷歌搜索,下面将其使用实例:
#包括<;iostream >;#包括<;sstream >;#包括<;升压/线程互斥。
HPP >;#包括<;升压/绑定。
HPP & gt;
#包括<;升压/线程池。
HPP & gt;
using namespace std;使用命名空间boost::线程池;
/ / / / helpersboost::互斥m_io_monitor;
无效的打印(文本){提升::::scoped_lock互斥锁(m_io_monitor);/ /每个线程使用全局互斥来保证每次只有一个线程执行cout <;<;文本;}
模板<;typename T & gt;串to_string(T const &;值){ ostringstream之上ost;OST <;<;价值;OST。
flush();返回的OST。
str();}
/ / / /例任务functionsvoid task_1() { print(“task_1() / N”);}
无效task_2() { print(“task_2() / N”);}
无效task_3() { print(“task_3() / N”);}
国际task_4() { print(“task_4() / N”);return 4;}
无效task_with_parameter(int值){打印(“task_with_parameter (“+ to_string(价值)+“)/ n”);}
国际循环= 0;布尔looped_task() { print(“looped_task() / N);返回+环& lt;5;}
国际task_int() { print(“task_int() / N”);return 23;}
无效fifo_pool_test() {池TP;
TP。
时间表(&;task_1);TP。
时间表(boost::绑定
(task_with_parameter,4));
如果(!TP。
empty()){ TP。
clear();/ /删除所有任务- >;本试验无输出}
size_t active_threads = TP。
active();size_t pending_threads = TP。
pending();size_t total_threads = TP。
size();
size_t假人= active_threads + pending_threads + total_threads;虚拟+ +;
TP。
size_controller()。
调整(5);TP。
wait();}
无效lifo_pool_test() { lifo_pool TP;TP。
size_controller()。
调整(0);计划(TP,&;task_1);TP。
size_controller()。
调整(10);TP。
wait();}
无效prio_pool_test() { prio_pool TP(2);计划(TP,
prio_task_func(1,&;task_1));计划(TP,prio_task_func (10,&;task_2));}
无效future_test() { fifo_pool TP(5);未来<;int & gt;FUT =计划(TP,&;task_4);int RES = fut();}
int main(int,char *const [ ]){ fifo_pool_test();
lifo_pool_test();prio_pool_test();future_test();return 0;}
任务返回值的获取:一般异步调用中,返回值的获取有同步获取和异步获取两种形式。
同步获取返回值:
国际task_int_23() { cout <;<;“task_int_23() / N”;return 23;}未来<;int & gt;分辨率=计划(TP,&;
task_int_23);研究wait();cout <;<;“物的价值:“<;<;水库get() <;<;endl;
异步获取返回值:
不知道是设计者就不打算使用异步回调获取返回值还是我看的不够仔细,异步获取返回值的方式还真没有找着,只好自己简单的写了一个回调的仿函数来实现异步返回值的获取。
/ / R为任务函数的返回值类型模板<;R类>;类callback_task { typedef boost::功能& lt;无效(R)& gt;回调;typedef boost::功能& lt;R()& gt;功能;私人:回调c_;功能f_;公共:/ / F:任务执行函数C:结果回调函数模板<;F类、C类>;callback_task (F,
C (c) {f _ = f; C _ = C;} void operator () () {C _ (f _ ());}};
通过这个对象可以很容易的实现异步结果的回调.
/ / task _ int _ 23的结果回调函数 void callback (int k) {cout & lt; & lt; "get callback value:" & lt; & lt; K & lt; & lt; endl;}
/ / 通过回调的形式获取任务的返回值 tp.schedule (callback _ task & lt; int & gt; & amp; task _ int _ 23, & amp; callback));
参考资料:
Boost官方网站: http: / / /。