Archive for December 27th, 2007

ThreadPool的使用

Thursday, December 27th, 2007

ThreadPool的使用

笔记: gashero

目录

1   简介

Nicky介绍给我使用的,其接口与其他很多线程池包装都差不多,不过因为只有一个模块,比较容易附带在程序中,所以研究下。 python threadpool 据介绍代码来自《Python in a Nutshell》的14.5节。

2   简单的使用

代码中给出的例子:

>>> pool=ThreadPool(poolsize)
>>> requests=makeRequests(some_callable,list_of_args,callback)
>>> [pool.putRequest(req) for req in requests
>>> pool.wait()

可见使用步骤如下:

  1. 建立线程池对象,其实是个线程管理器
  2. 建立计算任务对象Request
  3. 将计算任务对象放入线程池当中
  4. 等待计算完成

3   接口文档

英文见 http://chrisarndt.de/en/software/python/threadpool/api/

makeRequests(callable,args_list,callback=None,exc_callback=None)

创建多个计算请求,并允许有不同的参数。

参数列表中的每一个元素是两个元素的元组,分别是位置参数列表和关键字参数字典。

class ThreadPool

线程池类,发布工作请求并收集结果。

__init__(self,num_workers,q_size)

构造函数,设置线程池工作线程数量和最大任务队列长度。 num_workers 是初始化时的线程数量。如果 q_size>0 则会限制工作队列的长度,并且在工作队列满时阻塞继续插入工作请求的任务。

createWorkers(self,num_workers)

增加工作线程数量。

dismissWorkers(self,num_workers)

减少工作线程数量。

pool(self,block)

处理队列中的新结果。也就是循环的调用各个线程结果中的回调和错误回调。不过,当请求队列为空时会抛出 NoResultPending 异常,以表示所有的结果都处理完了。这个特点对于依赖线程执行结果继续加入请求队列的方式不太适合。

putRequest(self,request,block=True,timeout=0)

加入一个任务请求到工作队列。

wait(self)

等待执行结果,直到所有任务完成。

class WorkerThread

工作者线程,供ThreadPool内部使用,不必关注。其自定义方法也只有一个。

class WorkRequest

任务请求类。

__init__(self,callable,args=None,kwds=None,requestID=None,callback=None,exc_callback=None)

创建一个工作请求。

4   ThreadPool的递归任务管理问题

如果ThreadPool执行的任务中还会添加任务则需要多考虑几个问题。

如果一个这样的任务正在运行,尚未完成时任务列表就已经空了,那么ThreadPool会立即抛出 NoResultsPending 异常,以告知 wait() 方法所有任务都完成了。而事实上,还有一个线程尚未执行完成。

这种情况下,可以自己设置一个退出条件自己重新实现 wait() 方法。在循环中调用 poll(True) 方法。对于抛出的 NoResultsPending 异常视而不见。并自己设置循环的退出方法。

5   回调函数的使用

建立任务请求时有两种回调函数 callback 和 exc_callback ,他们的回调接口为:

callback(request,result)

exc_callback(request,sys.exc_info())

其中 request 为 WorkRequest 对象。而 result 则是调用线程函数正确的返回结果。 sys.exc_info() 为发生异常时返回的信息。 sys.exc_info() 是一个拥有3个元素的元组。分别为:

  • 异常类 :发生异常的类
  • 异常实例 :如上异常类的实例,包含更多详细信息
  • 跟踪信息 :traceback对象,可以显示错误的行号等等具体的错误信息

Warning

注意,如果没有设置 exc_callback 则发生异常时会将异常信息写入 callback 回调函数。如果同时没有设置 callback 和 exc_callback 则发生任何异常都不会有提示,根本无法调试。

5.1   使用 sys.exc_info() 信息

由于发生异常时返回的 sys.exc_info() 内容并不易读,所以可以用如下方式定制错误回调函数,将错误信息打印出来,或者可选的输出到日志文件。

import traceback
def exc_callback(excinfo):
    errorstr=''.join(traceback.format_exception(*excinfo))
    print errorstr

这样的显示结果就如同控制台中看到的错误跟踪一样了。