目录
进程:
生产者,消费者模型
在编程中,模型就是解决某个问题的固定方法或套路
生产者:泛指产生数据的一方
消费者:泛指处理数据的一方
生活案例:
食堂饭店是生产者,负责炒菜
吃饭的人就是消费者,负责吃
1. 要解决什么问题?
效率低,双方的处理速度不同,一方慢,一方快,则双方需要相互等待,比如:生产方一分钟能10个包子,它有十个盘子;但是消费者4分钟才能吃完一个馒头。生产方做完10个馒头,就会停止生产,因为盘子不够用,用时10分钟。消费者4分钟吃完一个馒头,也就是说生产方要到4分钟之后才能再生产一个馒头,依次类推。在这个案例中,其实生产方的效率是很高的,但是由于消费者的效率低,导致整个生产消费链时间拉长,那么对于这样的情况,我们如何解决了?第一:增加更多的盘子。第二:增加更多的消费者。第三:找一个大型的容器,对于生产方来说,它只需要把生产的馒头放到容器中即可;对于消费者来说,它只管从容器中拿东西即可。(这个大型的容器,就是下面引入的内容:队列)
2. 解决办法:在生产者和消费者之间加入队列
import time,randomimport multiprocessingdef product(q,name): for i in range(5): dog = "%s的热狗%s"%(name,(i+1)) time.sleep(random.random()) print("生产了",dog) q.put(i+1) q.put(None) # 生产者生产完毕后,在队列最后发送一个None,表示生产者生产完毕def customer(q,name): while True: dog = q.get() if not dog: # 判断生产何时结束 break time.sleep(random.random()) print("消费了%s的%s"%(name,dog))if __name__ == '__main__': q = multiprocessing.Queue() # 生产者 p1 = multiprocessing.Process(target=product,args=(q,"上海")) # p2 = multiprocessing.Process(target=product,args=(q,)) p1.start() # p2.start() # 消费者 c = multiprocessing.Process(target=customer,args=(q,"北京")) c.start() p1.join() c.join() print("结束")
问题:如果此时加入两个生产者,那么消费者如何判断两个生产者何时结束?
解决办法:joinableQueue
import multiprocessingimport time, random# 生产者def product(q, name): for i in range(5): dog = "%s的热狗%s" % (name, (i + 1)) time.sleep(random.random()) print("生产了", dog) q.put(dog)# 消费者def customer(q): while True: dog = q.get() time.sleep(random.random()) print("消费了%s" % (dog)) q.task_done()# dliffy diagranif __name__ == '__main__': q = multiprocessing.JoinableQueue() # 生产者 p1 = multiprocessing.Process(target=product, args=(q, "上海")) p2 = multiprocessing.Process(target=product, args=(q, "北京")) p1.start() p2.start() # 消费者 c = multiprocessing.Process(target=customer, args=(q,)) c.start() # 结束生产进程 p1.join() p2.join() # 结束队列进程 q.join() # 阻塞,等待队列进程任务执行完毕,再结束 # 由以上两点,可以确定消费者进程一定被消费完了 print("结束")
线程
1. 什么是线程
线程是操作系统可以运算调度的最小单位,是真正的执行单位,其包含在进程中。
一个线程就是一条固定的控制流程,一个进程可以包含多个线程
同一个进程中的线程共享进程内的资源
进程是操作系统可以调度或进程资源分配的基本单位,是一个资源单位,其中包含了运行这个程序所需的资源
2. 进程与线程的区别
进程是一个资源单位,线程是执行单位
创建进程的开销远大于线程
多个进程是隔离的,通信需要借助其他模块。而线程是共享进程内的所有资源
进程之间是竞争关系,而线程之间是协作关系
进程由主次关系,线程都是平等关系3. 为什么用线程
- 有多个任务需要并发处理
- 线程占用资源少,线程适用于任务数非常多的情况;进程占用资源多。
- 线程是共有进程内的资源
4. 如何使用多线程
第一种:简单模式
import threadingdef task(): print("子线程 run")# 与进程不同之处,不需要加main判断,开启线程的代码放哪里都可以t = threading.Thread(target=task)t.start()print("over")
第二种:自定义模式(继承)
import threadingclass MyThread(threading.Thread): def run(self): print("子,run") print("子,over")mt = MyThread()mt.start()print("主,over")"""
根据代码来验证线程的一些功能
第一:验证线程是否能进行并发
import threadingdef task(): while True: print("子") t = threading.Thread(target=task)t.start()while True: print("主")
子子子子子子主主主主主主子子子主主...
以上现象,即可说明线程是可以用来并发的执行的。
第二:验证线程和进程的开销问题
if __name__ == '__main__': # 线程开销,线程开了1000个 start_time = time.time() for i in range(1000): t = threading.Thread() t.start() print(time.time() - start_time) # 进程开销,进程只开了70个 start_time = time.time() for i in range(70): p = multiprocessing.Process() p.start() print(time.time() - start_time)
0.169901609420776370.5576796531677246
通过上面的示例,可以看出线程的开销要远远小于进程开销
第三:验证线程数据是否共享
import threadinga = 100def task(): global a for i in range(100): a -= 1t = threading.Thread(target=task)t.start()print(a)
0
可以看出,线程执行时,不会copy主进程的代码再从上自下执行,而是直接从主进程运行的地方开始执行。所以它能共享进程内的资源。
第四:验证多线程的pid是否相同
import threadingimport osdef task(): print(os.getpid())for i in range(100): a = threading.Thread(target=task) a.start()print(f"主 结束,{os.getpid()}")
1360013600136001360013600主 结束,13600
由此可见,多线程的pid是与主线程的pid相同的。
第五:多线程是否会存在争夺资源的安全问题
import threadingimport randomimport timea = 10def task(): global a temp = a time.sleep(random.random()) a = temp -1 a_list = []for i in range(10): t = threading.Thread(target=task) a_list.append(t) t.start()for i in a_list: i.join()print(a)
9
通过上面的实例,可以发现资源发生了争夺,那么我们应该避免了。
方案一:加锁
import threadingimport randomimport timea = 10s = threading.Lock() # 创建锁def task(): global a s.acquire() # 加锁 temp = a time.sleep(random.random()) a = temp - 1 s.release() # 解锁a_list = []for i in range(10): t = threading.Thread(target=task) a_list.append(t) t.start()for i in a_list: i.join()print(a)
0
这样就解决了争夺资源的问题
第六:验证多线程是否会随着主进程结束而结束
import threadingimport timeimport random# 妃子的一生def task(): print("妃子 start") time.sleep(3) print("妃子 over")# 皇帝的一生print("皇帝 start")t = threading.Thread(target=task)t.start()print("皇帝 over")
皇帝 start妃子 start皇帝 over妃子 over
这时,你会发现当皇帝over(主进程结束)之后,但是子进程依然没有消失。这种情况并不是我们期望的,我们希望主进程结束后,子进程也会跟随结束,那么对于这样的情况,我们的解决办法是什么了?
解决方案1:给子进程加守护进程
import threadingimport timeimport random# 妃子的一生def task(): print("妃子 start") time.sleep(3) print("妃子 over")# 皇帝的一生print("皇帝 start")t = threading.Thread(target=task)t.daemon = Truet.start()print("皇帝 over")
皇帝 start妃子 start皇帝 over
因此,我们知道了,默认情况下,主线程即使代码执行完毕,也会等待所有子线程(非守护线程)完毕后程序才能结束
多线程常用的属性
import threadingimport timet = threading.Thread()t.start()print(t.ident) # 线程的标识符 print(threading.currentThread()) # 打印当前进程对象t = threading.Thread(target=lambda : print(threading.currentThread()))t.start()t1 = threading.Thread(target=lambda : time.sleep(2))t1.start()print(threading.enumerate()) # 是一个列表容器,里面存放着所有存活的线程对象print(threading.active_count()) # 表示存活线程对象的个数
12764<_MainThread(MainThread, started 8976)>[<_MainThread(MainThread, started 8976)>, ]2