一、回顾
1、生产者消费者模型
主要为了解决强耦合的问题
2、队列 from multiprocessing import Queue
先进先出,队列本身是安全的
可连接的队列:from multiprocessing import JoinableQueue
task_done( ) 每消费一个数据,就返回一个标识
join( ) 接收task_done返回的标识,以便可以知道队列中的数据什么时候被消费完了
3、管道(了解)
本身不安全的
队列 = 管道 + 锁
from multiprocessing import Pipe
con1,con2 = Pipe( )
con1可以收发数据,con2也可以收发数据(全双工)
4、多进程之间共享内存数据
from multiprocessing import Manager,Value
5、进程池
from multiprocessing import Pool
p.map(func,iterable)
p.apply(func,args=( ))
p.apply_async(func, args=( ),callback=None)
close:不再接收新的任务,准备关闭
join:等待进程池中的所有进程执行完任务
池中的进程是守护进程,主进程的代码执行完毕,守护进程就结束了
在进程池中的回调函数是主进程调用的,和子进程无关。
二、线程
1、线程的概念
线程被称作轻量级的进程。 GIL:全局解释锁(只有Cpython解释器才有)
计算机的执行单位以线程为单位,计算机的最小可执行是线程。
进程是资源分配的基本单位。线程是可执行的基本单位,是可被调度的基本单位。
线程不可以自己独立拥有资源。线程的执行,必须依赖于所属进程中的资源。
线程又分为用户级线程和内核级线程:(了解)
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度。
内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。
2、线程和进程的比较
进程由:代码段、数据段、PCB组成
线程由:代码段、数据段、TCB组成
(1)初识线程
1 # *******************初识线程**********************开始 2 import threading 3 from threading import Thread 4 import time 5 6 def func(): 7 print("这是一个子线程") 8 time.sleep(2) 9 10 if __name__ == '__main__': 11 t = Thread(target=func,args=()) 12 t.start() 13 # *******************初识线程**********************结束
(2)线程与进程对比
① CPU切换进程要比CPU切换线程慢很多,在pyth中,如果IO操作过多的化话,使用多线程最好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # *******************线程与进程法比较**********************开始 2 from multiprocessing import Process 3 from threading import Thread 4 import time 5 6 def func(): 7 pass 8 9 if __name__ == '__main__': 10 start = time.time() 11 for i in range(100): 12 p = Process(target=func) 13 p.start() 14 print("开100个进程的时间:",time.time()-start) 15 16 start = time.time() 17 for i in range(100): 18 t = Thread(target=func) 19 t.start() 20 print("开100个线程的时间:", time.time() - start) 21 # *******************线程与进程法比较**********************结束
② 在同一个进程内,所有线程共享这个进程的pid,也就是说所有线程共享所属进程的所有资源和内存地址。、
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from multiprocessing import Process 2 from threading import Thread 3 import time,os 4 5 def func(name): 6 print("我是一个%s,我的pid是%s" % (name,os.getpid())) 7 8 if __name__ == '__main__': 9 print("我是main,我的的pid是:%s" % os.getpid()) 10 for i in range(10): 11 p = Process(target=func,args=('进程',)) 12 p.start() 13 14 for i in range(10): 15 p = Thread(target=func,args=('线程',)) 16 p.start()
③ 在同一个进程内,所有线程共享该进程的全局变量。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # *******************线程与进程的对比3**********************开始 2 from multiprocessing import Process 3 from threading import Thread 4 import time,os 5 6 def func(): 7 global num 8 num -= 1 9 10 if __name__ == '__main__': 11 num = 100 12 t = Thread(target=func) 13 t.start() 14 time.sleep(1) 15 print(num) # 99 16 # *******************线程与进程的对比3**********************结束
(3)线程之间的数据安全问题
因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行。
当你的任务是计算密集的情况下,使用多进程好。
【总结】在Cpython中,IO密集用多线程,计算密集用多进程。
3、死锁
(1)什么是死锁?如下代码
1 # *******************什么是死锁**********************开始 2 from multiprocessing import Process 3 from threading import Thread,Lock 4 import time,os 5 6 def man(l_tot,l_pap): 7 l_tot.acquire()# 是男的获得厕所资源,把厕所锁上了 8 print('alex在厕所上厕所') 9 time.sleep(1) 10 l_pap.acquire()# 男的拿纸资源 11 print('alex拿到卫生纸了!') 12 time.sleep(0.5) 13 print('alex完事了!') 14 l_pap.release()# 男的先还纸 15 l_tot.release()# 男的还厕所 16 17 def woman(l_tot,l_pap): 18 l_pap.acquire() # 女的拿纸资源 19 print('小雪拿到卫生纸了!') 20 time.sleep(1) 21 l_tot.acquire() # 是女的获得厕所资源,把厕所锁上了 22 print('小雪在厕所上厕所') 23 time.sleep(0.5) 24 print('小雪完事了!') 25 l_tot.release() # 女的还厕所 26 l_pap.release() # 女的先还纸 27 28 29 if __name__ == '__main__': 30 l_tot = Lock() 31 l_pap = Lock() 32 t_man = Thread(target=man,args=(l_tot,l_pap)) 33 t_woman = Thread(target=woman,args=(l_tot,l_pap)) 34 t_man.start() 35 t_woman.start() 36 # *******************什么是死锁**********************结束
(2)解决死锁问题——递归锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # *******************解决死锁**********************开始 2 '''RLock是递归锁——是无止尽的锁,但是所有锁都有一个共同的钥匙''' 3 4 from threading import Thread,RLock 5 import time,os 6 7 def man(l_tot,l_pap): 8 l_tot.acquire()# 是男的获得厕所资源,把厕所锁上了 9 print('alex在厕所上厕所') 10 time.sleep(1) 11 l_pap.acquire()# 男的拿纸资源 12 print('alex拿到卫生纸了!') 13 time.sleep(0.5) 14 print('alex完事了!') 15 l_pap.release()# 男的先还纸 16 l_tot.release()# 男的还厕所 17 18 def woman(l_tot,l_pap): 19 l_pap.acquire() # 女的拿纸资源 20 print('小雪拿到卫生纸了!') 21 time.sleep(1) 22 l_tot.acquire() # 是女的获得厕所资源,把厕所锁上了 23 print('小雪在厕所上厕所') 24 time.sleep(0.5) 25 print('小雪完事了!') 26 l_tot.release() # 女的还厕所 27 l_pap.release() # 女的先还纸 28 29 30 if __name__ == '__main__': 31 l_tot = l_pap = RLock() 32 t_man = Thread(target=man,args=(l_tot,l_pap)) 33 t_woman = Thread(target=woman,args=(l_tot,l_pap)) 34 t_man.start() 35 t_woman.start() 36 # *******************解决死锁**********************结束
【小结——锁】
递归锁:RLock( )可以有无止尽的锁,但是会有一把万能钥匙
互斥所:Lock( ) 一把钥匙配一把锁
GIL:全局解释器锁,锁的是线程,是解释器上的一个锁,锁的是线程,意思是在同一时间只允许一个线程访问CPU。
4、使用条件机制调度线程
from threading import Thread, Condition
Conditon( )涉及4个方法:
acquire( ) # 加锁
release( )# 解锁
wait( ) # 是指给wait发一个信号,让wait变成不阻塞
notify(int) # 是指给wait发一个信号,让wait变成不阻塞(int是指给多少wait发信号)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # *******************Conditon**********************开始 2 from threading import Condition,Thread 3 import time 4 5 def func(con,i): 6 con.acquire() 7 print(123) 8 con.wait() # 线程执行到这里,会阻塞住,等待notify发送信号,来唤醒此线程 9 print(456) 10 con.release() 11 print("第%s个线程开始执行了!" % i) 12 13 if __name__ == '__main__': 14 con = Condition() 15 for i in range(10): 16 t = Thread(target=func,args=(con,i)) 17 t.start() 18 19 while 1: 20 num = int(input("》》》")) 21 con.acquire() 22 con.notify(num) # 发送一个信号给num个正在阻塞在wait的线程,让这些线程正常执行 23 con.release() 24 # *******************Conditon**********************结束
5、线程里的定时器
1 # *******************定时器**********************开始 2 from threading import Timer 3 4 def func(): 5 print("开始执行了") 6 7 Timer(3,func).start() # 3秒后执行func 8 # *******************定时器**********************结束
6、线程里的事件机制
from threading import Event(与多进程的事件机制一样)
7、线程里的信号量
from threading import Semaphore(与多进程里的信号量一样)
8、线程里的守护线程
守护线程是根据主进程执行结束才结束
守护线程不是根据主线程的代码执行结束而结束
主线程会等待普通线程执行结束,再结束
守护线程会等待主线程结束,再结束。
1 # *******************守护线程**********************开始 2 from threading import Thread 3 import time 4 5 def func(): 6 time.sleep(2) 7 print(123) 8 9 def func1(): 10 time.sleep(3) 11 print(456) 12 13 if __name__ == '__main__': 14 t = Thread(target=func) 15 t.daemon = True # 守护线程 16 t.start() 17 t1 = Thread(target=func1) 18 t1.start() 19 # *******************守护线程**********************结束