Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5760378
  • 博文数量: 675
  • 博客积分: 20301
  • 博客等级: 上将
  • 技术积分: 7671
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 16:15
文章分类

全部博文(675)

文章存档

2012年(1)

2011年(20)

2010年(14)

2009年(63)

2008年(118)

2007年(141)

2006年(318)

分类: C/C++

2007-06-17 21:48:59

想找个轻便的 thread pool 实现,结果发现网上能找到的都是一些很重量级的,如 boost,ACE 里面的。唯有自己照着下面的需求实现了一个

源代码下载:

这个 threadpool 是基于 pthread api 写的。除了 linux/unix 上有 pthread 之外,windows 下也有 pthread 的库。

尝试把设计的过程写一下。由于设计过程中,思路是很跳跃的,有很多的思考细节可能都漏写了。

线程池也是一种对象池,和其他的对象池(如连接池)有相同的地方,
目的是在使用相关的对象时能够避免创建和销毁对象带来的资源消耗,使得程序响应更快。
但线程和其他普通的对象也有不同,线程包含一个指令指针,还包括其他资源,如运行时的函数激活记录的堆栈、一组寄存器和线程特有的数据。所以线程池的管理和其他对象池的管理就有很多的不同。

1.在开始设计线程池的时候,首先可以先考虑一下连接池的使用场景。
一般是由主线程主动地从池里面获取一个连接,主线程用完再把连接返回给池。
主线程对连接对象的使用,就是调用连接对象的方法(按 OO 的说法,就是给对象发送消息 )。

2.按连接池的使用场景,要实现线程池,首先要有一个线程对象。
当主线程从池里面获得一个线程之后,主线程要能够向这个线程发送消息,以达到主线程使用线程的目的。
那么这个线程对象需要能够支持主线程向线程池的线程发送消息。
通过阅读 pthread 的规范,可以了解到 pthread_cond_wait 是 POSIX 线程信号发送系统的核心。
主线程要发送给线程池线程的信息还包括这次任务的参数:一个函数指针和提供给这个函数的参数。

关于 pthread_cond_wait 的有关资料
http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread3/index.html

3.在上面的第二点中,只考虑了如何从线程池获得线程,并使用线程,而没有考虑如何在使用完线程之后如何把线程归还给池。
连接池的使用场景中,通常是有主线程来完成这个操作。但是在线程池的使用场景中,主线程在发送消息之后,
通常就不再等待这个线程完成任务。即主线程通常只负责取,但不负责还。
考虑到这一点,就需要在每个线程对象中记录自己所属的池,当完成任务之后,线程主动把自己归还到池里面。

至此我们可以设计出线程对象的数据结构

代码
  1. typedef struct _thread_st {  
  2.         pthread_t id;  
  3.         pthread_mutex_t mutex;  
  4.         pthread_cond_t cond;  
  5.         dispatch_fn fn;  
  6.         void *arg;  
  7.         threadpool parent;  
  8. } _thread;  

4.有了这个数据结构,对于 dispatch_threadpool 和 wrapper_fn 的设计也就比较容易想到了。

在 dispatch_threadpool 中,从池里面获得一个线程,然后设置要发送给线程的信息。
包括:thread->fn,thread->arg,thread->parent;接着正式发送
pthread_cond_signal( &thread->cond ) ;

代码
  1. int dispatch_threadpool(threadpool from_me, dispatch_fn dispatch_to_here, void *arg)    
  2. # {   
  3. ......  
  4. #        } else {    
  5. #                 pool->tp_index--;    
  6. #                 thread = pool->tp_list[ pool->tp_index ];    
  7. #                 pool->tp_list[ pool->tp_index ] = NULL;    
  8. #     
  9. #                 thread->fn = dispatch_to_here;    
  10. #                 thread->arg = arg;    
  11. #                 thread->parent = pool;    
  12. #     
  13. #                 pthread_mutex_lock( &thread->mutex );    
  14. #                 pthread_cond_signal( &thread->cond ) ;    
  15. #                 pthread_mutex_unlock ( &thread->mutex );    
  16. #        }   
  17. ......  
  18. }  

对于 dispatch_threadpool 再做一些说明。
1.上面只说明了 else 的那部分代码,else 前面的代码是创建线程的代码。
即这个线程池的实现使用了 lazy 的方式,不是在 create_threadpool 的时候立即创建所有线程,
而是在第一次使用的时候来创建线程。这样的做法,对于常见的应用来说,还是比较合适的。

2.当线程池里面没有可用的线程时(创建的线程数目已经达到指定的最大值,并且所有线程都在工作),
如果这个时候主线程分配任务,那么这个时候调用 dispatch_threadpool 将会被阻塞,直到有一个线程可用。

代码
  1. if( pool->tp_index <= 0 && pool->tp_total >= pool->tp_max_index ) {  
  2.   pthread_cond_wait( &pool->tp_idle, &pool->tp_mutex );  

在 wrapper_fn 中,每次执行 thread->fn( thread->arg ) 之后,主动把自己归还到池里面,
然后等待主线程发送下一次消息。

代码
  1. void * wrapper_fn( void * arg )    
  2. # {    
  3. ......  
  4. #        for( ; 0 == ((_threadpool*)thread->parent)->tp_stop; ) {   
  5. #                         thread->fn( thread->arg );   
  6. ......  
  7. #                         save_thread( thread->parent, thread );  
  8. #                         pthread_cond_wait( &thread->cond, &thread->mutex );    
  9. ......  
  10. #        }  
  11. ......  
  12. # }    

更多细节请参考完整的 。

阅读(1868) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~