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的更多相关文章

  1. ThreadPool.QueueUserWorkItem的用法

    代码: ThreadPool.SetMaxThreads(, ); ThreadPool.QueueUserWorkItem((obj) => { MessageBox.Show("执 ...

  2. C# - 多线程 之 Process与Thread与ThreadPool

    Process 进程类, // 提供对本地和远程进程的访问,启动/停止本地系统进程 public class Process : Component { public int Id { get; } ...

  3. C#多线程--线程池(ThreadPool)

    先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...

  4. mysql can't create threads in threadpool

    最近,我们在券商端的mysql运行一段时间后,发生mysql can't create threads in threadpool,如下所示: 据官网一个报告显示,目测是一个bug,内存紧张导致,那天 ...

  5. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  6. C#中Thread与ThreadPool的比较

    最近同事在编写一个基于UPD RTP协议的通信软件,在处理接收Listen时,发现了一个问题到底是用Thread还是ThreadPool呢? 我看同事的问题比较有典型性,还是做以整理培训一下吧 Thr ...

  7. python线程池(threadpool)模块使用笔记

    一.安装与简介 pip install threadpool pool = ThreadPool(poolsize) requests = makeRequests(some_callable, li ...

  8. Thread and ThreadPool

    C#中Thread与ThreadPool的比较 Thread类,一次使用一个线程,来创建和删除线程.这种方式建立和删除线程是很昂贵的(cpu密集型). Threadpool类 对于大多数的情况下是使用 ...

  9. 线程池ThreadPool知识碎片和使用经验速记

    ThreadPool(线程池)大概的工作原理是,初始时线程池中创建了一些线程,当应用程序需要使用线程池中的线程进行工作,线程池将会分配一个线程,之后到来的请求,线程池都会尽量使用池中已有的这个线程进行 ...

随机推荐

  1. Java中float/double取值范围与精度

    Java浮点数 浮点数结构 要说清楚Java浮点数的取值范围与其精度,必须先了解浮点数的表示方法,浮点数的结构组成,之所以会有这种所谓的结构,是因为机器只认识01,你想表示小数,你要机器认识小数点这个 ...

  2. 页面置换算 - FIFO、LFU、LRU

      缓存算法(页面置换算法)-FIFO.LFU.LRU 在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路,下面继续来探讨一下另外两种常见的Cache算法:FIFO.LFU 1 ...

  3. KMP + 求最小循环节 --- HUST 1010 - The Minimum Length

    The Minimum Length Problem's Link: http://acm.hust.edu.cn/problem/show/1010 Mean: 给你一个字符串,求这个字符串的最小循 ...

  4. ubuntu 按键替换 Control_R to Left

    ubuntu 按键替换 Control_R to Left 1 查看当前键盘布局 $xmodmap -pke keycode 105 = Control_R NoSymbol Control_Rkey ...

  5. PPT如何一页多张打印且铺满整个页面

    最近由于工作需要,有些ppt材料想打印出来学习,但是ppt页数较多,ppt单页打印有些浪费纸张,而且也不能拿到外面打印店去打印,所以只能自己动手设置一页多张打印,并且最后双面打印,这样就做够节省纸张了 ...

  6. C++关键字之explicit(显式)

     C++ Code  12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 ...

  7. WPF 隧道路由事件

    阅读本文前,请先了解 冒泡路由事件:http://www.cnblogs.com/andrew-blog/p/WPF_BubbledEvent.html 隧道路由事件的工作方式和冒泡路由事件相同,但方 ...

  8. WPF 纯代码生成界面(不使用XAML)

    对于编写 WPF 应用程序,只是用代码进行开发而不使用任何 XAML 不是常见的方式(但是仍然完全支持).只使用代码进行开发的明显缺点是,有可能会使用编写 WPF 应用程序成为极端乏味的工作. WPF ...

  9. Ubuntu执行su后输入密码结果认证失败--解决办法:sudo passwd修改命令

  10. 主线程不能执行耗时的操作,子线程不能更新Ui

    在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 在看方法之前看一下Android中消息机制: 引用 Message:消息 ...