python 进程
优点:可以处理大量的并发操作,使用IO计算型
缺点:由于进程之间的数据都是独立,所以创建一个进程,就得消耗一份内存 (进程和cpu核数相同的情况最好)
Process :进程 (让我想到了40个人,要烧40壶水,要弄40个炉子,但是效率高)
进程中有 join (2) 阻塞住啦,最多阻塞2秒钟;demaon(true) 可以设置不阻塞,直接运行。
都说进程之间的数据是独立,那么我们你能将进程之间的数据共享吗,聪明的人类,当然可以,那就用到了mange和array
arrary 这个东西 就是将一个列表转换成一个特殊的数据类型(数组),manage 方式数据共享
from multiprocessing import Process from multiprocessing import Manager import time if __name__ == '__main__': manage = Manager() dic = manage.list() def foo(i): dic[i]=100+i print (dic.values()) for i in range(10): p = Process(target=foo,args=(i,)) p.start() print(dic.values())
这个思路太混啦,不过老师说,这个进程数据共享用的不多,n
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如歌进程池中没有可供使用的进程池,那么程序就会等待,直到进程池有可用的进程为止
进程池有两个方法:
- apply(同步)
- apply_async(异步)
协程
线程和进程的操作是由程序触发接口,最后的执行者是系统,协程的操作则是程序员
协程存在的意义;对于多线程的应用,cpu通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序
协程的使用场景:当程序中存在大量不需要CPU的操作时(IO)
武老师 高级版线程池
#!/usr/bin/env python #-*- coding:utf-8 -*- import queue import threading import contextlib #创建一个终止符 StopEvent = object() #创建一个类 class ThreadPool(object): def __init__(self, max_num): #创建一个任务列队 self.q = queue.Queue(max_num) #定义这个任务队列的大小 self.max_num = max_num self.cancel = 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 True #如果等待的线程列表是空的,并且真实创建的线程列表小于任务的数量(创建的线程不可能大于任务数量) if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: #执行任务 self.generate_thread() #任务是一个元祖,其中callback是个回掉函数,也可以有,也可以没有 w = (func, args, callback,) #将任务放入队列 self.q.put(w) #执行任务 def generate_thread(self): #创建一个线程,去处理任务,(call就是处理任务的函数) t = threading.Thread(target=self.call) #启动该线程 t.start() #任务是一个元祖,用call方法来处理一个任务 def call(self): #获取当前的线程 current_thread = threading.currentThread #将线程加入线程池列表 self.generate_list.append(current_thread) #将获取的任务赋值给event event = self.q.get() #当获取的任务还是任务时(前边已经将任务封装成了元祖) while event != StopEvent: #将任务赋值给event 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 #将当前线程加入真实线程池列表 self.generate_list.append(current_thread) #然后再去获取任务 event = self.q.get() #因为这个线程还要去工作呢,所以将线程从线程池里面拿出来去执行任务 self.generate_list.remove(current_thread) else: #如果任务不是任务啦,从真实的线程里面移除这个线程 self.generate_list.remove(current_thread) def terminal(self): """ 终止线程池中的所有线程 """ self.cancel = True full_size = len(self.generate_list) while full_size: self.q.put(StopEvent) full_size -= 1 def stop(self): full_size = len(self.generate_list) while full_size: self.q.put(StopEvent) full_size -= 1 # # @contextlib.contextmanager # def worker_state(self, generate_list, current_thread): # """ # 用于记录线程中正在等待的线程数 # """ # generate_list.append(current_thread) # try: # yield # finally: # generate_list.remove(current_thread) def show(arg): import time # time.sleep(1) print (arg) #创建了最大为20个线程池 pool = ThreadPool(10) #定了500个任务 for i in range(100): pool.run(func=show, args=(i,)) pool.stop()
武老师的线程池关键点
1、创建了一个任务的队列
2、自己定了一个最大为多少的线程池 (只是定了最大的数量,并没有自己创建),这个数量根据你自己的配置,设计好了最优的数量
3、线程的创建不是人为创建的,是操作系统根据任务的耗时自己创建的
3.1如果人为的根据任务的数量去创建线程,有可能任务很短,有好多事用不到,那这样就造成了浪费
3.2这点让我想起了协程,协程不久认为代替操作系统去接管处理任务吗,如果能将协程代码嵌套进来,会不会更好,但是协程内部代码太复杂,搞不定。
4、闲置的线程被销毁,这个操作肯定python内部的解释器去销毁的,(要做的就是把这些协程搞成闲置的线程)