Python实现线程池

锁定老帖子  主题:Python实现线程池
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-03-02   最后修改:2011-05-03

最近在做一些文本处理方面的事情,考虑到程序利用并发性可以提高执行效率(不纠结特殊反例),于是入围的Idea如使用多进程或多线程达到期望的目标,对于进程或线程的创建是有代价的,那么我们是否可以实现一个线程池来达到已创建的线程反复使用从而使代价降低到最小呢?
当然可以,要想创建一个线程池,那么必须得有个容器来模拟“池”,在Python中,队列这样的数据结构就可以帮我们解决“池”这个问题,然而随之引来的 多线程之间数据处理同步问题,好在Python中有个Queue模块帮我们解决了这一棘手的问题,那么我们就可以实现一个线程池的雏形了。

Python代码   收藏代码
  1. # !/usr/bin/env python  
  2. # -*- coding:utf-8 -*-  
  3.   
  4. import Queue  
  5. import threading  
  6. import time  
  7.   
  8. class WorkManager(object):  
  9.     def __init__(self, work_num=1000,thread_num=2):  
  10.         self.work_queue = Queue.Queue()  
  11.         self.threads = []  
  12.         self.__init_work_queue(work_num)  
  13.         self.__init_thread_pool(thread_num)  
  14.   
  15.     """ 
  16.         初始化线程 
  17.     """  
  18.     def __init_thread_pool(self,thread_num):  
  19.         for i in range(thread_num):  
  20.             self.threads.append(Work(self.work_queue))  
  21.   
  22.     """ 
  23.         初始化工作队列 
  24.     """  
  25.     def __init_work_queue(self, jobs_num):  
  26.         for i in range(jobs_num):  
  27.             self.add_job(do_job, i)  
  28.   
  29.     """ 
  30.         添加一项工作入队 
  31.     """  
  32.     def add_job(self, func, *args):  
  33.         self.work_queue.put((func, list(args)))#任务入队,Queue内部实现了同步机制  
  34.   
  35.     """ 
  36.         等待所有线程运行完毕 
  37.     """     
  38.     def wait_allcomplete(self):  
  39.         for item in self.threads:  
  40.             if item.isAlive():item.join()  
  41.   
  42. class Work(threading.Thread):  
  43.     def __init__(self, work_queue):  
  44.         threading.Thread.__init__(self)  
  45.         self.work_queue = work_queue  
  46.         self.start()  
  47.   
  48.     def run(self):  
  49.         #死循环,从而让创建的线程在一定条件下关闭退出  
  50.         while True:  
  51.             try:  
  52.                 do, args = self.work_queue.get(block=False)#任务异步出队,Queue内部实现了同步机制  
  53.                 do(args)  
  54.                 self.work_queue.task_done()#通知系统任务完成  
  55.             except:  
  56.                 break  
  57.   
  58. #具体要做的任务  
  59. def do_job(args):  
  60.     time.sleep(0.1)#模拟处理时间  
  61.     print threading.current_thread(), list(args)  
  62.   
  63. if __name__ == '__main__':  
  64.     start = time.time()  
  65.     work_manager =  WorkManager(1000010)#或者work_manager =  WorkManager(10000, 20)  
  66.     work_manager.wait_allcomplete()  
  67.     end = time.time()  
  68.     print "cost all time: %s" % (end-start)  

 2次开启不同的线程数运行结果如下:

Python代码   收藏代码
  1. #work_manager =  WorkManager(10000, 10)  
  2. cost all time: 100.641790867(单位:秒)  
  3. #work_manager =  WorkManager(10000, 20)  
  4. cost all time:50.5233478546(单位:秒)  

 上面实现了线程池的雏形,展现了基本原理,当然要想成为通用的API需要做很多的工作,希望本文能够起到抛砖引玉的效果。

 
   发表时间:2011-03-10  
这个  太长了吧~ 
其实我还是怀念flex框架下的“多线程”式写法 

就是开num事件,然后在事件结束后判断是否满足运行完毕的条件 
如果是就跳出 如果不是 再来一轮 

可惜在线程中,似乎没有线程析构的说法 
也就是当线程完毕的时候 我们是拿不到信号的 (while 轮询? 不好) 
看到过一个简易版本的线程池  哪个简单干净 利于理解
0   请登录后投票
 
   发表时间:2011-03-11  

 最近做一个项目,也需要用到一个线程池,所以找了一个老外的开源项目中用的线程池代码,总共有 task, workthread, threadpool三大部分。不太清楚的是你说的queue是什么lib?

Python代码   收藏代码
  1. import threading  
  2.   
  3. class WorkerTask(object):  
  4.     """A task to be performed by the ThreadPool."""  
  5.   
  6.     def __init__(self, function, args=(), kwargs={}):  
  7.         self.function = function  
  8.         self.args = args  
  9.         self.kwargs = kwargs  
  10.   
  11.     def __call__(self):  
  12.         self.function(*self.args, **self.kwargs)  
  13.   
  14.   
  15. class WorkerThread(threading.Thread):  
  16.     """A thread managed by a thread pool."""  
  17.   
  18.     def __init__(self, pool):  
  19.         threading.Thread.__init__(self)  
  20.         self.setDaemon(True)  
  21.         self.pool = pool  
  22.         self.busy = False  
  23.         self._started = False  
  24.         self._event = None  
  25.   
  26.     def work(self):  
  27.         if self._started is True:  
  28.             if self._event is not None and not self._event.isSet():  
  29.                 self._event.set()  
  30.         else:  
  31.             self._started = True  
  32.             self.start()  
  33.   
  34.     def run(self):  
  35.         while True:  
  36.             self.busy = True  
  37.             while len(self.pool._tasks) > 0:  
  38.                 try:  
  39.                     task = self.pool._tasks.pop()  
  40.                     task()  
  41.                 except IndexError:  
  42.                     # Just in case another thread grabbed the task 1st.  
  43.                     pass  
  44.   
  45.             # Sleep until needed again  
  46.             self.busy = False  
  47.             if self._event is None:  
  48.                 self._event = threading.Event()  
  49.             else:  
  50.                 self._event.clear()  
  51.             self._event.wait()  
  52.   
  53. class ThreadPool(object):  
  54.     """Executes queued tasks in the background."""  
  55.   
  56.     def __init__(self, max_pool_size=10):  
  57.         self.max_pool_size = max_pool_size  
  58.         self._threads = []  
  59.         self._tasks = []   
  60.   
  61.     def _addTask(self, task):  
  62.         self._tasks.append(task)  
  63.   
  64.         worker_thread = None  
  65.         for thread in self._threads:  
  66.             if thread.busy is False:  
  67.                 worker_thread = thread  
  68.                 break  
  69.   
  70.         if worker_thread is None and len(self._threads) <= self.max_pool_size:  
  71.             worker_thread = WorkerThread(self)  
  72.             self._threads.append(worker_thread)  
  73.   
  74.         if worker_thread is not None:  
  75.             worker_thread.work()  
  76.   
  77.     def addTask(self, function, args=(), kwargs={}):  
  78.         self._addTask(WorkerTask(function, args, kwargs))  
  79.   
  80. class GlobalThreadPool(object):  
  81.     """ThreadPool Singleton class."""  
  82.   
  83.     _instance = None  
  84.   
  85.     def __init__(self):  
  86.         """Create singleton instance """  
  87.   
  88.         if GlobalThreadPool._instance is None:  
  89.             # Create and remember instance  
  90.             GlobalThreadPool._instance = ThreadPool()  
  91.   
  92.     def __getattr__(self, attr):  
  93.         """ Delegate get access to implementation """  
  94.         return getattr(self._instance, attr)  
  95.   
  96.     def __setattr__(self, attr, val):  
  97.         """ Delegate set access to implementation """  
  98.         return setattr(self._instance, attr, val)

线程池的研究及实现

什么是线程池?

诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创建一个新的服务对象,然后在新的服务对象中为请求服务。但当有大量请求并发访问时,服务器不断的创建和销毁对象的开销很大。所以提高服务器效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这样就引入了“池”的概念,“池”的概念使得人们可以定制一定量的资源,然后对这些资源进行复用,而不是频繁的创建和销毁。

线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。

线程池的注意事项

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。在使用线程池时需注意线程池大小与性能的关系,注意并发风险、死锁、资源不足和线程泄漏等问题。

(1)线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。

(2)并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。

(3)线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。

简单线程池的设计

一个典型的线程池,应该包括如下几个部分:
1、线程池管理器(ThreadPool),用于启动、停用,管理线程池
2、工作线程(WorkThread),线程池中的线程
3、请求接口(WorkRequest),创建请求对象,以供工作线程调度任务的执行
4、请求队列(RequestQueue),用于存放和提取请求
5、结果队列(ResultQueue),用于存储请求执行后返回的结果

线程池管理器,通过添加请求的方法(putRequest)向请求队列(RequestQueue)添加请求,这些请求事先需要实现请求接口,即传递工作函数、参数、结果处理函数、以及异常处理函数。之后初始化一定数量的工作线程,这些线程通过轮询的方式不断查看请求队列(RequestQueue),只要有请求存在,则会提取出请求,进行执行。然后,线程池管理器调用方法(poll)查看结果队列(resultQueue)是否有值,如果有值,则取出,调用结果处理函数执行。通过以上讲述,不难发现,这个系统的核心资源在于请求队列和结果队列,工作线程通过轮询requestQueue获得人物,主线程通过查看结果队列,获得执行结果。因此,对这个队列的设计,要实现线程同步,以及一定阻塞和超时机制的设计,以防止因为不断轮询而导致的过多cpu开销。在本文中,将会用python语言实现,python的Queue,就是很好的实现了对线程同步机制。

使用Python实现:

复制代码
#-*-encoding:utf-8-*-
'''
Created on 2012-3-9
@summary: 线程池
@contact: mailto:zhanglixinseu@gmail.com
@author: zhanglixin
'''
import sys
import threading
import Queue
import traceback

# 定义一些Exception,用于自定义异常处理

class NoResultsPending(Exception):
"""All works requests have been processed"""
pass

class NoWorkersAvailable(Exception):
"""No worket threads available to process remaining requests."""
pass

def _handle_thread_exception(request, exc_info):
"""默认的异常处理函数,只是简单的打印"""
traceback.print_exception(*exc_info)

#classes

class WorkerThread(threading.Thread):
"""后台线程,真正的工作线程,从请求队列(requestQueue)中获取work,
并将执行后的结果添加到结果队列(resultQueue)
"""
def __init__(self,requestQueue,resultQueue,poll_timeout=5,**kwds):
threading.Thread.__init__(self,**kwds)
'''设置为守护进行'''
self.setDaemon(True)
self._requestQueue = requestQueue
self._resultQueue = resultQueue
self._poll_timeout = poll_timeout
'''设置一个flag信号,用来表示该线程是否还被dismiss,默认为false'''
self._dismissed = threading.Event()
self.start()

def run(self):
'''每个线程尽可能多的执行work,所以采用loop,
只要线程可用,并且requestQueue有work未完成,则一直loop
'''
while True:
if self._dismissed.is_set():
break
try:
'''
Queue.Queue队列设置了线程同步策略,并且可以设置timeout。
一直block,直到requestQueue有值,或者超时
'''
request = self._requestQueue.get(True,self._poll_timeout)
except Queue.Empty:
continue
else:
'''之所以在这里再次判断dimissed,是因为之前的timeout时间里,很有可能,该线程被dismiss掉了'''
if self._dismissed.is_set():
self._requestQueue.put(request)
break
try:
'''执行callable,讲请求和结果以tuple的方式放入requestQueue'''
result = request.callable(*request.args,**request.kwds)
print self.getName()
self._resultQueue.put((request,result))
except:
'''异常处理'''
request.exception = True
self._resultQueue.put((request,sys.exc_info()))

def dismiss(self):
'''设置一个标志,表示完成当前work之后,退出'''
self._dismissed.set()


class WorkRequest:
'''
@param callable_:,可定制的,执行work的函数
@param args: 列表参数
@param kwds: 字典参数
@param requestID: id
@param callback: 可定制的,处理resultQueue队列元素的函数
@param exc_callback:可定制的,处理异常的函数
'''
def __init__(self,callable_,args=None,kwds=None,requestID=None,
callback=None,exc_callback=_handle_thread_exception):
if requestID == None:
self.requestID = id(self)
else:
try:
self.requestID = hash(requestID)
except TypeError:
raise TypeError("requestId must be hashable")
self.exception = False
self.callback = callback
self.exc_callback = exc_callback
self.callable = callable_
self.args = args or []
self.kwds = kwds or {}

def __str__(self):
return "WorkRequest id=%s args=%r kwargs=%r exception=%s" % \
(self.requestID,self.args,self.kwds,self.exception)

class ThreadPool:
'''
@param num_workers:初始化的线程数量
@param q_size,resq_size: requestQueue和result队列的初始大小
@param poll_timeout: 设置工作线程WorkerThread的timeout,也就是等待requestQueue的timeout
'''
def __init__(self,num_workers,q_size=0,resq_size=0,poll_timeout=5):
self._requestQueue = Queue.Queue(q_size)
self._resultQueue = Queue.Queue(resq_size)
self.workers = []
self.dismissedWorkers = []
self.workRequests = {} #设置个字典,方便使用
self.createWorkers(num_workers,poll_timeout)

def createWorkers(self,num_workers,poll_timeout=5):
'''创建num_workers个WorkThread,默认timeout为5'''
for i in range(num_workers):
self.workers.append(WorkerThread(self._requestQueue,self._resultQueue,poll_timeout=poll_timeout))

def dismissWorkers(self,num_workers,do_join=False):
'''停用num_workers数量的线程,并加入dismiss_list'''
dismiss_list = []
for i in range(min(num_workers,len(self.workers))):
worker = self.workers.pop()
worker.dismiss()
dismiss_list.append(worker)
if do_join :
for worker in dismiss_list:
worker.join()
else:
self.dismissedWorkers.extend(dismiss_list)

def joinAllDismissedWorkers(self):
'''join 所有停用的thread'''
#print len(self.dismissedWorkers)
for worker in self.dismissedWorkers:
worker.join()
self.dismissedWorkers = []

def putRequest(self,request ,block=True,timeout=None):
assert isinstance(request,WorkRequest)
assert not getattr(request,'exception',None)
'''当queue满了,也就是容量达到了前面设定的q_size,它将一直阻塞,直到有空余位置,或是timeout'''
self._requestQueue.put(request, block, timeout)
self.workRequests[request.requestID] = request

def poll(self,block = False):
while True:
if not self.workRequests:
raise NoResultsPending
elif block and not self.workers:
raise NoWorkersAvailable
try:
'''默认只要resultQueue有值,则取出,否则一直block'''
request , result = self._resultQueue.get(block=block)
if request.exception and request.exc_callback:
request.exc_callback(request,result)
if request.callback and not (request.exception and request.exc_callback):
request.callback(request,result)
del self.workRequests[request.requestID]
except Queue.Empty:
break

def wait(self):
while True:
try:
self.poll(True)
except NoResultsPending:
break

def workersize(self):
return len(self.workers)

def stop(self):
'''join 所有的thread,确保所有的线程都执行完毕'''
self.dismissWorkers(self.workersize(),True)
self.joinAllDismissedWorkers()
复制代码

测试代码:

复制代码
#Test a demo

if __name__=='__main__':
import random
import time
import datetime
def do_work(data):
time.sleep(random.randint(1,3))
res = str(datetime.datetime.now()) + "" +str(data)
return res

def print_result(request,result):
print "---Result from request %s : %r" % (request.requestID,result)

main = ThreadPool(3)
for i in range(40):
req = WorkRequest(do_work,args=[i],kwds={},callback=print_result)
main.putRequest(req)
print "work request #%s added." % req.requestID

print '-'*20, main.workersize(),'-'*20

counter = 0
while True:
try:
time.sleep(0.5)
main.poll()
if(counter==5):
print "Add 3 more workers threads"
main.createWorkers(3)
print '-'*20, main.workersize(),'-'*20
if(counter==10):
print "dismiss 2 workers threads"
main.dismissWorkers(2)
print '-'*20, main.workersize(),'-'*20
counter+=1
except NoResultsPending:
print "no pending results"
break

main.stop()
print "Stop"
复制代码

参考文献:
1、程序原型:Threadpool —— http://chrisarndt.de/projects/threadpool/ 

2、王华,马亮,顾明.线程池技术研究与应用[J].计算机应用研究

3、李昊,刘志镜.线程池技术的研究[J].现代电子技术

4、赵海,李志蜀,韩学为,叶浩.线程池的优化设计[J].四川大学学报(自然科学版)

 

附:Python的多线程问题

python 的GIL规定每个时刻只能有一个线程访问python虚拟机,所以你要用python的多线程来做计算是很不合算的,但是对于IO密集型的应用,例如网络交互来说,python的多线程还是非常给力的。
如果你是一个计算密集型的任务,非要用python来并行执行的话,有以下几个方法:
1 使用python的multiprocessing 模块,能够发挥多核的优势。
2 使用ironPython,但是这个只能在windows下用
3 使用pypy,这个可以实现真正的多线程。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值