简单版

  

import queue
import threading class ThreadPool(object): def __init__(self, max_num=20):
self.queue = queue.Queue(max_num)
for i in range(max_num):
self.queue.put(threading.Thread) def get_thread(self):
return self.queue.get() def add_thread(self):
self.queue.put(threading.Thread) #实例化线程池对象
pool = ThreadPool(10)
#执行构造方法--实例一个max_num=10的队列,并往队列上添加10个线程类 def func(arg, p):
print(arg)
import time
time.sleep(2)
#在线程执行完前往队列里再加上一个线程类
p.add_thread() #生成30个任务
for i in range(30):
#每生成一个任务就从队列里取出一个线程类
thread = pool.get_thread()
#并实例化线程对象来执行任务
t = thread(target=func, args=(i, pool))
#线程启动后,执行func函数
t.start()

  我们可以看到,简单版的线程池就是简单的,逻辑就是每次执行一个任务就从队列取一个线程类创建一个线程,所以就有了--多少个任务,多少个线程,那和进程池相比较下,你会从中发现哪些不足呢?

  • 第一,首先问你,执行完的线程去哪呢?--被程序回收销毁了!   那么执行完这些任务有必要一对一的创建线程吗??--如果任务执行的快,一个线程有时候可以做多个任务
  • 第二,上面只规定了队列的长度,并没有规定线程池里的线程数量??
  • 第三,进程池有回调函数这么一说法,上面没有?
  • 第四,进程池里提供了close和terminate方法...

绝版

  

  好!我们来看看代码,是怎样解决上面不足的:

import queue
import threading
import contextlib
import time #放入队列里,做为线程停止运行的信号
StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None):
#对max_task_num进行判断
if max_task_num:
#如果有值传入,在创建队列时,就按传入值限定队列长度
self.q = queue.Queue(max_task_num)
else:
#否则,就默认为队列长度为无限长
self.q = queue.Queue()
#最大线程数
self.max_num = max_num
#close方法的状态码
self.cancel = False
#terminate方法的状态码
self.terminal = False
#生成线程列表
self.generate_list = []
#空闲线程列表
self.free_list = [] def run(self, func, args, callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
""" #如果执行close方法,这里就会执行return--中止函数,等同不再创建线程
if self.cancel:
return
#如果空闲线程列表里为空并且已生成的线程数没有超过规定的最大线程数
if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
#那么就调用generate_thread方法创建线程
self.generate_thread()
#打包任务
w = (func, args, callback,)
#把打包的任务放入到队列里
self.q.put(w) def generate_thread(self): #创建了一个线程
t = threading.Thread(target=self.call)
#启动线程,执行call方法
t.start() def call(self):
"""
循环去获取任务函数并执行任务函数
""" #获取当前线程变量
current_thread = threading.currentThread
#把当前线程添加到生成线程列表里
self.generate_list.append(current_thread) #到队列里去拿任务
event = self.q.get()
#对取到的任务进行判断
while event != StopEvent:
#如果任务不等于停止信号,进入循环 #解任务包
func, arguments, callback = event
#尝试执行任务包里的函数
try:
#执行成功,拿到result执行结果
result = func(*arguments)
#并给执行状态赋值True
success = True
except Exception as e:
#执行失败,赋值执行状态False
success = False
#同时赋值执行结果为None
result = None if callback is not None:
#如果callback不是默认None,就尝试下列操作
try:
#把执行状态和执行结果传给回调函数,执行
callback(success, result)
except Exception as e:
pass #with上下文管理--等同把free_list,current_thread传给worker_state方法,并执行
with self.worker_state(self.free_list, current_thread):
#判断self.terminal的状态
if self.terminal:
#如果状态为True,就把任务变量设置为停止信号,等同于中止当前线程
event = StopEvent
else:
#否则,就去队列里取任务
event = self.q.get()
else:
#如果是停止信号,就把当前线程从生成线程列表中移除
self.generate_list.remove(current_thread) def close(self):
"""
执行完所有的任务后,所有线程停止
""" #当执行close方法时,就把cancel状态设置为True
self.cancel = True
full_size = len(self.generate_list)
while full_size:
#往队列里加停止信号,直到生成线程列表长度为0
self.q.put(StopEvent)
full_size -= 1 def terminate(self):
"""
无论是否还有任务,终止线程
""" #当执行terminate方法时,把terminal状态设置为True
self.terminal = True
#因为突然中止,难免队列里还有任务,所以先清空一下队列
self.q.empty()
while self.generate_list:
#往队列里加停止信号,值到生成线程列表为空时
self.q.put(StopEvent) @contextlib.contextmanager
def worker_state(self, state_list, worker_thread):
"""
用于记录线程中正在等待的线程数
""" #把执行完任务的当前线程添加到空闲线程列表里
state_list.append(worker_thread)
try:
#暂时跳出
yield
finally:
#把当前线程从空闲线程列表里移除
state_list.remove(worker_thread) # How to use pool = ThreadPool(5) def callback(status, result):
# status, execute action status
# result, execute action return value
pass def action(i):
print(i) for i in range(30):
ret = pool.run(action, (i,), callback) time.sleep(5)
print(len(pool.generate_list), len(pool.free_list))
print(len(pool.generate_list), len(pool.free_list))
# pool.close()
# pool.terminate()

           欢迎大家对我的博客内容提出质疑和提问!谢谢

                                                                             笔者:拍省先生

python基础-第九篇-9.3线程池的更多相关文章

  1. python基础-第九篇-9.2线程与多线程

    单线程 import time beginTime = time.time() for a in range(10): print(a) time.sleep(1) shijian = time.ti ...

  2. python基础-第九篇-9.1初了解Python线程、进程、协程

    了解相关概念之前,我们先来看一张图 进程: 优点:同时利用多个cpu,能够同时进行多个操作 缺点:耗费资源(重新开辟内存空间) 线程: 优点:共享内存,IO操作时候,创造并发操作 缺点:抢占资源 通过 ...

  3. Python 基础学习篇

    注:技术尚浅,时间匆忙,如有错误或者不当之处值得商榷的,请留言,吾必思而改之. 第一篇 :Python基础- 安装/变量/输入/及循环语句使用 第二篇:  Python基础- 常用数据类型 第三篇: ...

  4. python第十一天-----补:线程池

    低版本: #!/usr/bin/env python import threading import time import queue class TreadPool: ""&q ...

  5. Python并发复习4- concurrent.futures模块(线程池和进程池)

    Python标准库为我们提供了threading(多线程模块)和multiprocessing(多进程模块).从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提 ...

  6. python(13)多线程:线程池,threading

    python 多进程:多进程 先上代码: pool = threadpool.ThreadPool(10) #建立线程池,控制线程数量为10 reqs = threadpool.makeRequest ...

  7. java并发编程JUC第九篇:CountDownLatch线程同步

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...

  8. 第九章 Java中线程池

    Java中的线程池是运用场景最多的并发框架,几乎所有需求异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...

  9. Java基础知识(多线程和线程池)

    新建状态: 一个新产生的线程从新状态开始了它的生命周期.它保持这个状态直到程序 start 这个线程. 运行状态:当一个新状态的线程被 start 以后,线程就变成可运行状态,一个线程在此状态下被认为 ...

随机推荐

  1. HTC Desire 816刷机教程(图文)

    HTC Desire 816刷机教程也来了,今天在这里主要是来说说如何刷机的,这个刷机是采用卡刷的方式,也就是利用第三方的recovery来刷入第三方的zip包,因为第三方的zip包都是支持卡刷的,很 ...

  2. hdu 4463 Outlets

    #include<bits/stdc++.h> using namespace std; double x[100+5],y[100+5]; double e[100+5][100+5]; ...

  3. 基础控制器MVC ,全局判断

    public class BaseController : Controller    {        //        // GET: /Base/ protected override voi ...

  4. 如何在浏览器控制台(console)里输出彩色样式调试信息

    console.log(XX,XX,XX) log 的第一个参数声明第二.第三个参数的作用,第二个参数就是样式,第三个参数是要输出的字符串 console.log("%c%s", ...

  5. 对ChemDraw Professional 16.0你了解多少

    ChemDraw Professional 16.0组件为科学家提供了最新的科学智能应用程序组件,绘制化学结构图和分析生物路径图.  ChemDraw Professional 16.0 ChemDr ...

  6. 编辑框添加灰色提示字(html+VC)

    Html中添加灰色提示字,使用属性placeholder即可! <input type="text" placeholder="要显示的文字"> 但 ...

  7. mysql数据库中,查看数据库的字符集(所有库的字符集或者某个特定库的字符集)

    需求描述: mysql中,想要查看某个数据库的字符集.通过information_schma模式下的schemata表来查询 环境描述: mysql版本:5.7.21-log 操作过程: 1.查看in ...

  8. 如何使用CodeSmith批量生成代码(原创系列教程)

    在上一篇我们已经用PowerDesigner创建好了需要的测试数据库,下面就可以开始用它完成批量代码生成的工作啦. 下面我会一步步的解释如何用CodeSmith实现预期的结果的,事先声明一下,在此只做 ...

  9. [转]ASP.NET MVC 5 - 给数据模型添加校验器

    在本节中将会给Movie模型添加验证逻辑.并且确保这些验证规则在用户创建或编辑电影时被执行. 拒绝重复 DRY ASP.NET MVC 的核心设计信条之一是DRY: "不要重复自己(DRY  ...

  10. 详解JavaScript的splice()方法

    from:http://www.jquerycn.cn/a_10447 在javascript中splice()方法,是一个很强的数组方法,它有多种用法.splice()主要用途是向数组的中部插入项. ...