Pyton(八)2、进程、线程、协程 _ 复习归纳篇
一、自己的问题
1.为什么要有三个程,没有他们代码也可以执行呀!!
2.三者的区别及各自的优缺点。
3.三者的相关代码重新敲一遍。
可以浏览下面网址,作者详细阐明了三者的作用及区别。
http://blog.csdn.net/u012332571/article/details/53419216
三个程的原理就是切换着去做所有的任务(比喻如下):
假设你每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。
如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,一次全部做完,一共花5小时,这种方式称为单任务模型,或者批处理任务模型。
假设你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以幼儿园小朋友的 眼光来看,你就正在同时写5科作业。
但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切 换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切 换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假 死状态。所以,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。
1.为什么要有三个程,没有他们代码也可以执行呀!!
答: 三个程的存在就是为了让函数同时执行,假如没有它们,func()函数要10分钟才执行完,func2()...以此类推,那么所有的函数执行完毕需要非常多的时间,
有了三个程,就可以让他们同时执行,大大缩短了时间。
2.三者的不同及各自的优缺点。
进程:数据之间不共享,每个进程都独占一份内存,最高效的工作方式是 进程数 == 计算机核数。
线程:数据共享,适合IO密集型任务、cpu消耗少的任务,如web。
协程(异步IO):数据共享,它就是一个线程,但是可以执行多个任务,没有切换的开销,不需要多线程的锁机制,也不存在变量冲突。
3.三程的代码
进程:
t = multiprocessing.Process()
t.start()
需要注意,在windows下面需要进行name的判断
import multiprocessing a = 0 def f(b): global a a += b print(a) if __name__ == '__main__': for iter in range(3): t = multiprocessing.Process(target=f,args=(1,)) t.start() """ #输出结果 1 1 1 可以看出全局变量a的值并没有得到改变,说明他们之间的不存在共享 """
Manager的数据共享
#调用Manager 可以把任务中的数据放在里面,且它们是共享的。 from multiprocessing import Manager,Process import time def func(type,i): type.append(i) if __name__ == '__main__': obj = Manager() lis = obj.list() #dic = obj.dict() #tup = obj.tuple() for iter in range(3): t = Process(target=func,args=(lis,iter)) t.start() #t.join() #等待任务全部执行完成,因为进程也是并发的所以,只有进程完全结束后,你才可以调用它们的结果。 time.sleep(1) #睡眠也是一样,当睡眠长度要超过执行的长度才可以取值成功。 print(lis)
线程:
t = threading.Thread()
t.start()
from threading import Thread A = 0 def func(a): global A A = A + a print(A) for iter in range(5): t = Thread(target=func, args=(iter,)) t.start() #从结果可以看出,线程之间的数据是共享的。 """ 输出结果 0 1 3 6 10 """
t.setDaemon()
#setDaemon(True) 时终止任务。 from threading import Thread import time def func(a): print("Hello",a) time.sleep(0.001) print("lazy boy",a) for iter in range(10): t = Thread(target=func,args=(iter,)) t.setDaemon(True) #True False 两个必须填写一个,False代表什么也不干,True 表示执行 t.start() #从结果可以看出,setDaemon()t.qq.com 为True时,终止任务。 """ 输出结果: Hello 0 Hello 1 Hello 2 Hello 3 Hello 4 Hello 5 Hello 6 Hello 7 Hello 8 lazy boy 1 lazy boy 0 Hello 9 """
t.join()
from threading import Thread import time def func(a): print("Hello",a) time.sleep(1) print("lazy boy",a) for iter in range(10): t = Thread(target=func,args=(iter,)) t.start() t.join() #函数一步一步的执行,让多线程毫无意义。 """ 输出结果 Hello 0 lazy boy 0 Hello 1 lazy boy 1 Hello 2 lazy boy 2 Hello 3 lazy boy 3 Hello 4 lazy boy 4 Hello 5 lazy boy 5 Hello 6 lazy boy 6 Hello 7 lazy boy 7 Hello 8 lazy boy 8 Hello 9 lazy boy 9 """
Evnet()
#做用阻塞 from threading import Thread,Event import time def func(event,a): print("Hello") event.wait() #阻塞任务。 print(a) event_obj = Event() for iter in range(10): t = Thread(target=func, args=(event_obj,iter)) t.start() inp = input(">>>") if inp == "1": event_obj.set() #从结果可以看出一旦下了命令,就开始执行被阻塞的任务。 #event_obj.clear() 清除set """ Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello 9 1 4 2 5 7 8 3 0 6 """
线程锁
作用:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。
#因为线程是并发的并且数据共享,如果多个线程同时对数据进行修改,就会产生脏数据,锁的出现就是为了,不让线程同时修改。 import threading import time globals_num = 0 lock = threading.RLock() def Func(): #lock.acquire() # 获得锁 global globals_num globals_num += 1 time.sleep(1) print(globals_num) #lock.release() # 释放锁 for i in range(10): t = threading.Thread(target=Func) t.start()
queue 队列:
往队列里面放东西,put 往队列里面取东西 get 当队列放满时,会一直处于等待状态,利用并发,当管有空位置时,又开始放,取东西 当队列为空时,也会一直等待。
#队列就是好比一根管,可以往管里面放东先也可以取东西。 import queue,threading q = queue.Queue(maxsize=3) #为了0时队列长度无限, q.put("你好") #put方法是往队列里面放东西 q.put("xxx") #全部存在管里面 q.put("xxx") #当put的东西超出管的长度是会等待。 q.put("xxx") for i in range(3): #利用循环取出来 g = q.get() #get <= put 不等待 print(g) #get > put 等待