threadpool源码学习

__all__ = [
'makeRequests',
'NoResultsPending',
'NoWorkersAvailable',
'ThreadPool',
'WorkRequest',
'WorkerThread'
]

WorkerThread

WorkerThread(requests_queue, results_queue) 将实例化一个线程对象,实例化后就会执行自身的run方法。run方法里执行一个while循环,一直去requests_queue取任务(WorkRequest对象),如果获取到就执行WorkRequest对象的callable(*request.args, **request.kwds)方法并将返回赋值给result, 然后把结果放到_results_queue里,放进去格式为元组(request, result),如果执行过程中遇到异常就将request.exception设置为True并将异常信息放入。dismiss方法通过将Event对象设置为True的方式将run中的while循环break掉。代码如下:

class WorkerThread(threading.Thread):

    def __init__(self, requests_queue, results_queue, poll_timeout=5, **kwds):
threading.Thread.__init__(self, **kwds)
self.setDaemon(1)
self._requests_queue = requests_queue
self._results_queue = results_queue
self._poll_timeout = poll_timeout
self._dismissed = threading.Event()
self.start() def run(self):
while True:
if self._dismissed.isSet():
break
try:
request = self._requests_queue.get(True)
except Queue.Empty:
continue
else:
if self._dismissed.isSet():
self._requests_queue.put(request)
break
try:
result = request.callable(*request.args, **request.kwds)
self._results_queue.put((request, result))
except:
request.exception = True
self._results_queue.put((request, sys.exc_info())) def dismiss(self):
self._dismissed.set()

WorkRequest

WorkRequest()将实例化一个request对象,实例化时会设置一个可hash的requestID值。callable_是将要被线程执行的可callable的对象,callback是执行完任务后执行的回调方法,exc_callback是任务执行异常时的回调。代码如下:

class WorkRequest:

    def __init__(self, callable_, args=None, kwds=None, requestID=None,
callback=None, exc_callback=_handle_thread_exception):
if requestID is 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)

makeRequests

makeRequests是一个函数,makeRequests()调用后会返回一个requests列表,这里args_list里元素有两种,如果只有位置参数那么传入request对象时就是(位置参数),如果既有位置参数又有关键字参数那么传入request对象就是(位置参数)+ {关键字参数字典}。代码如下:

def makeRequests(callable_, args_list, callback=None,
exc_callback=_handle_thread_exception):
requests = []
for item in args_list:
if isinstance(item, tuple):
requests.append(
WorkRequest(callable_, item[0], item[1], callback=callback,
exc_callback=exc_callback)
)
else:
requests.append(
WorkRequest(callable_, [item], None, callback=callback,
exc_callback=exc_callback)
)
return requests

ThreadPool

ThreadPool(num_workers)将实例化一个有num_workers数量线程的线程池对象,线程池的线程共用_requests_queue和_results_queue两个队列,当执行putRequest(request)时就会将request放入_requests_queue这个队列,这时候池内的线程就能从_requests_queue获取到值并执行了,要获取结果需要执行poll()方法取到一个结果就会将workRequests{requestID: request}这个字典里request删除掉,最后取完后就会有NoResultsPending异常。这里如果调用wait方法将会等待所有结果。代码如下:

class ThreadPool:

    def __init__(self, num_workers, q_size=0, resq_size=0, poll_timeout=5):
self._requests_queue = Queue.Queue(q_size)
self._results_queue = Queue.Queue(resq_size)
self.workers = []
self.dismissedWorkers = []
self.workRequests = {}
self.createWorkers(num_workers, poll_timeout) def createWorkers(self, num_workers, poll_timeout=5):
for i in range(num_workers):
self.workers.append(WorkerThread(self._requests_queue,
self._results_queue, poll_timeout=poll_timeout)) def dismissWorkers(self, num_workers, do_join=False):
"""Tell num_workers worker threads to quit after their current task."""
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):
"""Perform Thread.join() on all worker threads that have been dismissed.
"""
for worker in self.dismissedWorkers:
worker.join()
self.dismissedWorkers = [] def putRequest(self, request, block=True, timeout=None):
"""Put work request into work queue and save its id for later."""
assert isinstance(request, WorkRequest)
# don't reuse old work requests
assert not getattr(request, 'exception', None)
self._requests_queue.put(request, block, timeout)
self.workRequests[request.requestID] = request def poll(self, block=False):
"""Process any new results in the queue."""
while True:
# still results pending?
if not self.workRequests:
raise NoResultsPending
# are there still workers to process remaining requests?
elif block and not self.workers:
raise NoWorkersAvailable
try:
# get back next results
request, result = self._results_queue.get(block=block)
# has an exception occured?
if request.exception and request.exc_callback:
request.exc_callback(request, result)
# hand results to callback, if any
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):
"""Wait for results, blocking until all have arrived."""
while 1:
try:
self.poll(True)
except NoResultsPending:
break

源码中的测试example

我觉得这个example太舒服了,看注释就差不多都能看懂了,就不多说了,毕竟talk is cheap!

if __name__ == '__main__':
import random
import time # the work the threads will have to do (rather trivial in our example)
def do_something(data):
time.sleep(random.randint(1,5))
result = round(random.random() * data, 5)
# just to show off, we throw an exception once in a while
if result > 5:
raise RuntimeError("Something extraordinary happened!")
return result # this will be called each time a result is available
def print_result(request, result):
print("**** Result from request #%s: %r" % (request.requestID, result)) # this will be called when an exception occurs within a thread
# this example exception handler does little more than the default handler
def handle_exception(request, exc_info):
if not isinstance(exc_info, tuple):
# Something is seriously wrong...
print(request)
print(exc_info)
raise SystemExit
print("**** Exception occured in request #%s: %s" % \
(request.requestID, exc_info)) # assemble the arguments for each job to a list...
data = [random.randint(1,10) for i in range(20)]
# ... and build a WorkRequest object for each item in data
requests = makeRequests(do_something, data, print_result, handle_exception)
# to use the default exception handler, uncomment next line and comment out
# the preceding one.
#requests = makeRequests(do_something, data, print_result) # or the other form of args_lists accepted by makeRequests: ((,), {})
data = [((random.randint(1,10),), {}) for i in range(20)]
requests.extend(
makeRequests(do_something, data, print_result, handle_exception)
#makeRequests(do_something, data, print_result)
# to use the default exception handler, uncomment next line and comment
# out the preceding one.
) # we create a pool of 3 worker threads
print("Creating thread pool with 3 worker threads.")
main = ThreadPool(3) # then we put the work requests in the queue...
for req in requests:
main.putRequest(req)
print("Work request #%s added." % req.requestID)
# or shorter:
# [main.putRequest(req) for req in requests] # ...and wait for the results to arrive in the result queue
# by using ThreadPool.wait(). This would block until results for
# all work requests have arrived:
# main.wait() # instead we can poll for results while doing something else:
i = 0
while True:
try:
time.sleep(0.5)
main.poll()
print("Main thread working...")
print("(active worker threads: %i)" % (threading.activeCount()-1, ))
if i == 10:
print("**** Adding 3 more worker threads...")
main.createWorkers(3)
if i == 20:
print("**** Dismissing 2 worker threads...")
main.dismissWorkers(2)
i += 1
except KeyboardInterrupt:
print("**** Interrupted!")
break
except NoResultsPending:
print("**** No pending results.")
break
if main.dismissedWorkers:
print("Joining all dismissed worker threads...")
main.joinAllDismissedWorkers()

threadpool源码学习的更多相关文章

  1. Java并发包源码学习系列:同步组件Semaphore源码解析

    目录 Semaphore概述及案例学习 类图结构及重要字段 void acquire() 非公平 公平策略 void acquire(int permits) void acquireUninterr ...

  2. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  3. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  8. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  9. ddms(基于 Express 的表单管理系统)源码学习

    ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...

随机推荐

  1. Autofac之类型关联

    前面的学习一直使用的是直接注册类型并不是Autofac已经依赖注入的主要使用方式,最佳的依赖注入与Autofac的使用方式,都是要结合面向接口(抽象)编程的概念的.推崇的是依赖于抽象而不是具体 pub ...

  2. cnblogs

    想注册个博客园来着的,看着大佬们的博客都十分漂亮,但是发现我因为太菜没有办法搞定美化问题. 以后再说吧 写写东西,反正也没人看,但是写的时候尽量按给别人看的格式写吧 2019.3.15 开通博客 计划 ...

  3. linux系统运维命令

    1.动态查看网卡流量  sar -n DEV 1 2.查看当前网卡的buffer size情况 ethtool -g eth0 3.修改当前网卡的buffer size ethtool -G eth0 ...

  4. python:Crypto模块的下载

    1.下载 Crypto 用于一些加密算法,需要引入的模块 目前做支付宝接口的时候会用到 pip3 install pycryptodome # 该方式是正常的 快速方式:pip3 install -i ...

  5. C# Asp.net中简单操作MongoDB数据库(二)

    C# Asp.net中简单操作MongoDB数据库(一)    , mongodb数据库连接可以回顾上面的篇幅. 1.model类: public class BaseEntity { /// < ...

  6. Hadoop集群测试wordcount程序

    一.集群环境搭好了,我们来测试一下吧 1.在java下创建一个wordcount文件夹:mkdir wordcount 2.在此文件夹下创建两个文件,比如file1.txt和file2.txt 在fi ...

  7. python练习题-day20

    1.json.pickle.shelve三个区别是什么? 首先,这三个模块都是序列化工具. 1. json是所有语言的序列化工具,优点跨语言.体积小.只能序列化一些基本的数据类型.int\str\li ...

  8. 改造一下jeecg中的部门树

    假装有需求 关于 jeecg 提供的部门树,相信很多小伙伴都已经用过了,今天假装有那么一个需求 "部门树弹窗选择默认展开下级部门",带着这个需求再次去探索一下吧. 一.改造之前的部 ...

  9. hystrix参数使用方法

    hystrix+feign+ribbon,但是可能很多人都知道hystrix还有线程隔离,信号量隔离,等等各种参数配置,在这几就记录下hystrix的参数, 一.hystrix参数使用方法 通过注解@ ...

  10. ORA-00600: 内部错误代码, 参数: [kcm_headroom_warn_1], [], [], [], [], [], [], [], [], [], [], []

    SQL*Plus: Release 11.2.0.4.0 Production on 星期三 1月 1 08:53:48 2003 Copyright (c) 1982, 2013, Oracle. ...