Loki之ThreadPool
Loki中的ThreadPool目的主要是对创建出来的线程进行复用。
ThreadPool在Test而非Loki目录下,因此并非是标准Loki的组件之一,不过我们可以对其修改定制,
下面是对其源码的大致分析,ThreadPool顾名思义线程池,一般我们使用线程的时候CreateThread调用我们的回调函数,当回调函数结束之后我们的线程也随之终结销毁,
这样出现的问题是我们想执行一个多线程任务都要CreateThread,我们是否能够CreateThread调用我们的回调函数结束之后暂时将这个线程保存
起来而不是直接调回,当下次我们想要执行一个多线程任务的时候只需要把回调函数传递给该多线程对象,让它再次调用我们新的回调函数,这个
就类似与STL中的内存池对内存的复用,只是STL内存池会对整块大内存进行分片链接到链表处理而已。
想要真正了解ThreadPool最好的办法是浏览源代码
首先是线程对象Thread,它有以下几种状态:
enum Status
{
Dead = 0,
Idle,
Starting,
Active
};
在创建线程并启动的时候起内部调用回调函数是
#if defined( _MSC_VER )
unsigned int ThreadPool::TopFunction( void * p )
#else
void * ThreadPool::TopFunction( void * p )
#endif
{
assert( nullptr != p );
volatile Thread * thread = reinterpret_cast< volatile Thread * >( p );
Thread::SetCurrentThread( thread );
while ( ( thread->m_status != Thread::Dead ) && ( !thread->m_stop ) )
{
// Call the thread's WaitPolicy here?
#if defined( _MSC_VER )
::SleepEx( 1, true );
#else
::sleep( 1 );
#endif
if ( thread->m_status == Thread::Starting ) //如果线程状态变为Starting,说明要复用该线程,调用用户的回调函数
{
try
{
assert( nullptr != thread->m_func );
thread->m_status = Thread::Active;
thread->m_func( thread->m_parm );
}
catch ( ... )
{
// What to do in case of exception?
// Call an exception policy?
}
//用户回调函数运行结束后将线程状态设置为空闲,以便下次复用
thread->m_status = Thread::Idle;
thread->m_func = nullptr;
thread->m_parm = nullptr;
}
}
#if defined( _MSC_VER )
return 0;
#else
return nullptr;
#endif
}
//该函数判读的是用户的回调函数是否执行结束,而非ThreadPool传递给它的TopFunction是否结束,
//返回true仅仅说明用户的回调函数执行结束,实际该线程依然存在于内存池中
bool Thread::WaitForThread( void ) volatile
{
assert( IsValid( m_owner ) );
const volatile Thread * current = Thread::GetCurrentThread();
if ( this == current )
return false;
if ( m_status == Thread::Dead )
return false;
while ( this->m_status == Thread::Active )
{
// Call the wait policy.
#if defined( _MSC_VER )
::SleepEx( 1, true );
#else
::sleep( 1 );
#endif
}
return true;
}
下面介绍ThreadPool,ThreadPool使用vector对线程进行保存,主要列举关键的几个函数进行说明
//创建threadCount个多线程于内存池中
unsigned int ThreadPool::Create( unsigned int threadCount ) volatile
{
assert( IsValid() );
LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; )
ThreadPool * pThis = const_cast< ThreadPool * >( this );
//计算内存池中Idle状态的线程数目,如果不够客户要求则创建足够的线程放到线程池中
const unsigned int countNow = GetCount( Thread::Idle );
if ( threadCount <= countNow )
return threadCount;
const unsigned int totalCount = pThis->m_threads.size();
const unsigned int howManyToAdd = threadCount - countNow;
if ( pThis->m_threads.capacity() <= howManyToAdd )
pThis->m_threads.reserve( totalCount + howManyToAdd );
for ( unsigned int ii = 0; ii < howManyToAdd; ++ii )
{
#if defined( _MSC_VER )
volatile Thread * thread = new Thread( this );
#else
Thread * thread = new Thread( this );
#endif
pThis->m_threads.push_back( thread );
Thread * pThread = const_cast< Thread * >( thread );
void * p = reinterpret_cast< void * >( pThread );
// Call thread creation policy? 此处可以调用创建线程策略,留给读者进行改进
LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p );
}
return howManyToAdd;
}
//开始执行用户的回调函数,function为用户的回调函数,parm为传递给function的void指针
volatile Thread * ThreadPool::Start( CallFunction function, void * parm ) volatile
{
assert( IsValid() );
LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; )
ThreadPool * pThis = const_cast< ThreadPool * >( this );
if ( nullptr == function )
return nullptr;
#if defined( _MSC_VER )
volatile Thread * thread = nullptr;
#else
Thread * thread = nullptr;
#endif
bool foundOne = false;
//查找内存池中是否有idle线程
for ( size_t ii = 0; ii < pThis->m_threads.size(); ii++ )
{
#if defined( _MSC_VER )
thread = pThis->m_threads.at( ii );
#else
thread = const_cast< Thread * >( pThis->m_threads.at( ii ) );
#endif
assert( nullptr != thread );
if ( Thread::Idle == thread->m_status )
{
foundOne = true;
break;
}
}
//存在空闲线程的话进行复用,否则创建一个线程并放入内存池vector中,
//对相应参数赋值,设置线程状态为Starting之后在内存池回调函数TopFunction中会自动调用用户回调函数function,线程状态设置为active,
//function结束之后在将线程状态设置为idle,这样就可以达到线程复用的效果
if ( foundOne )
{
thread->m_func = function;
thread->m_parm = parm;
thread->m_status = Thread::Starting;
}
else
{
// Did not find an idle thread, so start a new one.
thread = new Thread( this, function, parm );
pThis->m_threads.push_back( thread );
Thread * pThread = const_cast< Thread * >( thread );
void * p = reinterpret_cast< void * >( pThread );
// Call to thread creation policy?
LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p );
}
return thread;
}
该线程池比较容易理解与使用,但是觉得不太好的地方就是TopFunction中循环的SleepEx部分,以及线程创建之后必须要再ThreadPool析构之后才会销毁,在Start的时候如果找不到idle线程
就会创建一个新的线程push_back到vector中,不能够保证控制线程在一个数量下。
设计改进:
用户希望内存池最多用n个线程,Start函数根据当前的线程数量可以在找不到idle线程的时候创建线程,但是达到n之后不能够在创建,这样我们可以把用户的function和parm保存
到一个队列当中,当线程池中的线程有idle线程的时候我们从任务队列中取出元素执行之
Loki之ThreadPool的更多相关文章
- ThreadPool.QueueUserWorkItem的用法
代码: ThreadPool.SetMaxThreads(, ); ThreadPool.QueueUserWorkItem((obj) => { MessageBox.Show("执 ...
- C# - 多线程 之 Process与Thread与ThreadPool
Process 进程类, // 提供对本地和远程进程的访问,启动/停止本地系统进程 public class Process : Component { public int Id { get; } ...
- C#多线程--线程池(ThreadPool)
先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...
- mysql can't create threads in threadpool
最近,我们在券商端的mysql运行一段时间后,发生mysql can't create threads in threadpool,如下所示: 据官网一个报告显示,目测是一个bug,内存紧张导致,那天 ...
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...
- C#中Thread与ThreadPool的比较
最近同事在编写一个基于UPD RTP协议的通信软件,在处理接收Listen时,发现了一个问题到底是用Thread还是ThreadPool呢? 我看同事的问题比较有典型性,还是做以整理培训一下吧 Thr ...
- python线程池(threadpool)模块使用笔记
一.安装与简介 pip install threadpool pool = ThreadPool(poolsize) requests = makeRequests(some_callable, li ...
- Thread and ThreadPool
C#中Thread与ThreadPool的比较 Thread类,一次使用一个线程,来创建和删除线程.这种方式建立和删除线程是很昂贵的(cpu密集型). Threadpool类 对于大多数的情况下是使用 ...
- 线程池ThreadPool知识碎片和使用经验速记
ThreadPool(线程池)大概的工作原理是,初始时线程池中创建了一些线程,当应用程序需要使用线程池中的线程进行工作,线程池将会分配一个线程,之后到来的请求,线程池都会尽量使用池中已有的这个线程进行 ...
随机推荐
- Linux samba 服务的配置
今天有个学生问我 samba 服务怎么配置,所以晚上特意研究一下怎么配置这个服务. 过程如下: sudo apt-get install samba samba-common // 安装 samba ...
- Java反射机制的基本概念与使用
本篇文章分为以下几个部分: 1.认识反射 2.反射的源头(Class类) 3.利用反射操作构造方法 4.利用反射调用类中的方法 5.反射中的invoke方法 6.利用反射调用类中的属性 反射在我们普通 ...
- saveFileDialog对话框
private void button1_Click(object sender, EventArgs e) { saveFileDialog1.Filter = "*.txt|*.txt| ...
- http协议之报文详解
一. 概述 用于HTTP协议交互的信息被称为HTTP报文.请求端(客户端)的http报文叫做请求报文,响应端的叫做响应报文. 报文,是网络中交换和传输的数据单元,即站点一次性要发送的数据块.报文包含了 ...
- rsync+inotify-tools
源服务器:192.168.0.100 目标服务器:192.168.0.101 目的:把源服务器上/home/test目录实时同步到目标服务器的/home/test下 具体操作: 第一部 ...
- Memcached内存分配及使用问题
在启动memcached的时候可以加-f参数和-n参数.-f指定各slab里面chunk大小的变化比例,默认1.25,-n指定slab里面chunk大小从多少开始.使用memcache_add($me ...
- Dalvikvm工具
dalvikvm:创建一个虚拟机并运行參数指定的java类. 1.新建一个java文件:例如以下: public class Test_07_22 { public static void main( ...
- 关于Unity中的新手编码技巧
写代码遇到报错,问题怎么办?怎么查看unity代码的接口?函数参数不记得了怎么办? 解决方法: 1.选择不懂的函数或类,按F12,跳转到代码的定义,自己去看就可知道了. 2.有的时候,选择一个函数,按 ...
- 第二百七十七节,MySQL数据库-数据表、以及列的增删改查
MySQL数据库-数据表.以及列的增删改查 1.创建一个表 CREATE(创建) TABLE(表) ENGINE(引擎) ENGINE=INNODB(引擎)还有很多类引擎,这里只是简单的提一下INNO ...
- Generator生成器函数
接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还有一种常用的解决方案,它就是Ge ...