第二十八篇、自定义线程池
一、自定义线程池
自定义线程池逻辑图
#!/usr/bin/env python #-*- coding:utf-8 -*- import threading import queue """ 1、写了一个for循环搞300个任务,然后pool.run方法,之后给元祖里面添加任务,满足条件创建一个线程, 紧接着第二个循环又进来了,第一个线程不一定能够执行完毕,然后满足条件再创建一个线程, 接着第三次循环又见来了,这个时候如果前两个线程执行完毕之后,那么为了提高效率,也就不满足判断,就不会 继续创建线程,而是用前两个线程取任务直接执行 2、线程取的任务就是元祖,这里要思考个问题,就是怎么判断任务被取完成了(任务被封存到元祖里面了) 元祖是任务,其他是终止符 3、想要终止 --思路 查看还有多少线程,就增加多少个终止符,然后最后再执行close方法 1)让正在从队列中取任务的线程挂掉 2)主线程,你跳我就跳 4、让当前的任务立即执行,即不管后面还有多少个任务,直接现在线程结束执行 """ StopEvent = object() #这个是停止标志,但线程执行到队列中这里的时候说明这个停止标志是最后一个,也就是任务执行完毕了 class ThreadPool(): def __init__(self,max_num): self.q = queue.Queue() #创建一个队列,没有指定参数,说明可以接受无限个东西 self.max_num = max_num #最多创建的线程数(线程池最大容量) self.generate_list = [] #真实创建的线程列表 self.free_list = [] #空闲的线程数量 self.terminal = False #默认为false的时候不终止 def run(self,func,args,callback=None): ''' 线程池执行一个任务,func要干什么,args参数 ''' w = (func,args,callback,) #封装到元祖里面 self.q.put(w) #把任务封装到队列里面 if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: #如果创建的线程等于0或者小于最大线程数 self.generate_thread() def generate_thread(self): ''' 创建一个线程 ''' t = threading.Thread(target=self.call) #创建线程调用call方法 t.start() def call(self): """ 循环去获取任务函数并执行任务函数 """ current_thread = threading.current_thread()#获取当前线程 self.generate_list.append(current_thread) #把线程放入到这个列表中 event = self.q.get() #获取任务并且执行 while event !=StopEvent: #如果不是停止标志那么就是任务 #是元祖 ==》任务 #解开任务包 #执行任务 #线程执行任务可能会出错 func,args,callback = event #event就是任务,任务也就是元祖,元祖里面有三个元素 status =True try: ret = func(args) #执行func函数,循环里面执行func函数,参数传入就执行任务函数 except Exception as e: status = False #把标志发给回调函数 ret = e #把错误信息发给回调函数 if callback == None: #设置是否有毁掉函数,有了就传入,没有就不传 pass else: callback(status,ret) #把上面函数结果传递给回调函数 if self.terminal: #如果为false就取任务 event = StopEvent else: #如果任务全部执行了之后 应该标记:我空闲了 self.free_list.append(current_thread) #任务完成之后把线程添加到空闲任务里面 event = self.q.get() self.free_list.remove(current_thread) #如果取到任务,那么就把这个线程从空闲列表中移除 else: #不是元祖,不是任务,这个时候就应该把执行停止标志的线程移除掉 self.generate_list.remove(current_thread) def close(self):#添加终止符 """ 思路:看结尾还有多少个线程就增加多少个终止符 """ num = len(self.generate_list) while num: self.q.put(StopEvent) num -=1 def terminate(self): #终止线程,(不清空队列) self.terminal=True while self.generate_list: self.q.put(StopEvent) self.q.queue.clear() # #清空队列,先清空队列然后终止线程 # self.q.empty() # #有可能出现等待的线程,所以要把全部线程终止 # max_num = len(self.genarate_list) # while max_num: # self.q.put(StopEvent) # max_num -=1 import time def action(i): time.sleep(0.5) print(i) pool = ThreadPool(10) for item in range(50):#这里的300个就是任务,将任务放置在队列中 """ 开始处理任务 - 创建线程 - 有空闲线程则不再创建线程 - 如果没有空闲线程,就创建线程 ,不能高于线程池的限制 - 根据任务个数判断,如果任务个数低于线程池数,那么就创建任务个数线程,如果高于那么就创建线程池数个线程 - 线程去队列中取任务 """ pool.run(func=action,args=(item,)) time.sleep(0.1) pool.terminate() # time.sleep # print(len(pool.generate_list)) # pool.close()(2)
#!/usr/bin/env python # -*- coding:utf-8 -*- import queue import threading import contextlib import time StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None): if max_task_num: self.q = queue.Queue(max_task_num) else: self.q = queue.Queue() self.max_num = max_num self.cancel = False 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 """ if self.cancel: return if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: self.generate_thread() w = (func, args, callback,) self.q.put(w) def generate_thread(self): """ 创建一个线程 """ t = threading.Thread(target=self.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 = func(*arguments) success = True except Exception as e: success = False result = None if callback is not None: try: callback(success, result) except Exception as e: pass with self.worker_state(self.free_list, current_thread): if self.terminal: event = StopEvent else: event = self.q.get() else: self.generate_list.remove(current_thread) def close(self): """ 执行完所有的任务后,所有线程停止 """ self.cancel = True full_size = len(self.generate_list) while full_size: self.q.put(StopEvent) full_size -= 1 def terminate(self): """ 无论是否还有任务,终止线程 """ self.terminal = True while self.generate_list: self.q.put(StopEvent) self.q.queue.clear() @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()
二、上下文管理之with自定义open
要用自定义上下文管理必须要用到下面的装饰器
#!/usr/bin/env python # -*- coding:utf-8 -*- import contextlib import queue @contextlib.contextmanager def worker_state(xxx,val): xxx.append(val) try: yield 123 finally: xxx.remove(val) q = queue.Queue() q.put("aa") li = [] with worker_state(li,2) as f: print(f) q.get()
1 """ 2 上面执行流程 3 1、先创建队列q 4 2、往队列里面放入值 5 3、往下执行with方法,执行worker_state(li,1): 函数 6 4、执行xxx.append(val),往xxx里面放入值 7 5、执行yield方法,然后执行get方法,从队列里面取值 8 6、最后执行remove方法 9 """
执行顺序
扩展:
包含yield 关键字的函数成为一个迭代器,yield跟return的位置一样,只不过每次返回结果后,并没有退出,而是等待下一次迭代,下一次迭代开始后从yield后的语句开始执行,直到再次遇到yield,返回新一次的结果。