Day 33 并发编程3
目錄
- Day 33 并發編程3
- 生產者消費者模型
- 要解決什么問題
- 用途
- 多線程
- 什么是線程
- 進程對比線程
- 為什么用線程
- 使用線程
- 線程安全問題
- 守護線程
- 線程中的常用方法
- 生產者消費者模型
Day 33 并發編程3
生產者消費者模型
模型就是解決某個問題的固定方法或者套路
要解決什么問題
- 生產者:泛指生產數據的一方
- 消費者:負責把處理數據的一方
案例:
? 食堂飯店是生產者
? 吃飯的人是消費者
他們之間的問題:
? 效率低,因為雙方的處理速度不一致,雙方需要相互等待
具體的解決方案
- 先將雙方解開耦合,讓不同的進程負責不同的任務
- 提供一個共享容器,來平衡雙方的能力,之所以用進程隊列是因為隊列可以在進程間共享
案例
from multiprocessing import Process,Queue
import requests,time,random,re,osdef product(urls,q):i=1for url in urls:respone=requests.get(url)text=respone.text# time.sleep(random.random())q.put(text)print(os.getpid(),f'got NO{i} info')i+=1def customer(q):i=1while True:text=q.get()time.sleep(random.random())res=re.findall('src=//(.*?) width', text)print(f'got NO{i} img')i++1if __name__ == '__main__':urls = ["http://www.baidu.com","http://www.baidu.com","http://www.baidu.com","http://www.baidu.com",]q=Queue()p=Process(target=product,args=(urls,q))p.start()c=Process(target=customer,args=(q,))c.start() 問題:customer進程不知道什么時候結束
joinableQueue繼承自Queue,用法一致
增加了join和taskDone
join是一個阻塞函數,會阻塞直到taskdone的調用次數等于存入的元素個數,可以用于表示隊列任務處理完成
from multiprocessing import Process,JoinableQueue
import requests,time,random,re,osdef product(q,name):for i in range(5):hot_dog=f'hot_dog{i+1} from {name}'time.sleep(random.random())q.put(hot_dog)print(f'{name} make hot_dog{i+1}')def customer(q):while True:hot_dog=q.get()time.sleep(random.random())print(f'eat {hot_dog}')q.task_done()if __name__ == '__main__':q=JoinableQueue()p1=Process(target=product,args=(q,'shanghai'))p1.start()p2=Process(target=product,args=(q,'beijing'))p2.start()c=Process(target=customer,args=(q,))c.start()p1.join()p2.join()q.join()c.terminate() 用途
常用來做流量削峰,保證服務不會因為高并發而崩潰
多線程
什么是線程
進程是操作系統可以調度和資源分配的基本單位,是一個資源單位,其中包含了運行這個程序所需的資源
線程是操作系統可以運算調度的最小單位,是真正的執行單位,一個線程就是一條固定的控制流程
一個進程可以包含多個線程,統一進程中的線程共享進程內的資源
系統會為每一個進程自動創建一條線程,稱之為主線程,后續通過代碼開啟的線程稱之為子線程
進程對比線程
進程是一個資源單位 而線程是執行單位
創建進程的開銷大于線程
多個進程之間的內存是相互隔離的,而線程是共享進程內的所有資源
進程間是競爭關系,而線程間是合作關系
開啟線程也需要消耗資源
進程之間有子父級關系
為什么用線程
- 有多個任務要并發處理
- 當要并發處理的任務有很多的時候,不能使用進程,進程資源開銷大
使用線程
方式一:直接實例化thread類
方式二:繼承thread類
from threading import Threaddef task():print('son run')t=Thread(target=task) #子線程不需要重新讀取主線程的代碼 所以開啟線程的代碼可以隨便放在需要的位置
t.start()
print('over')
# 線程開啟速度快 所以先打印son run 后打印overclass MyThread(Thread):def run(self) -> None:print(' son run')t=MyThread()
t.start()
print('over') 線程安全問題
線程并發訪問了同一資源一定會產生安全問題,解決方案和進程一樣,就是給公共資源加鎖
from threading import Thread,Lock
import timel=Lock()
a=10def task():global al.acquire()time.sleep(1)a-=1print(a)l.release()for i in range(10):t=Thread(target=task)t.start()print(f'{i} work')
# 瞬間開啟10個線程,但是同一時間只有1個線程可以執行函數代碼 守護線程
一個線程a設置為b的守護線程,a會隨著b的結束而結束
默認情況下主線程代碼執行完畢,也會等待所有子線程執行完畢后才能結束,因為多個線程之間是協作關系
from threading import Thread,Lock
import timedef task1():print('this is taks1')time.sleep(5)print('task1 going to die')def task2():print('this is task2')time.sleep(4)print('task2 not dead')print('main will be dead in 3 seconds')
t1=Thread(target=task1)
t1.daemon=True
t1.start()
t2=Thread(target=task2)
t2.daemon=False
t2.start()time.sleep(1)
print('main dead') 線程中的常用方法
from threading import Thread,currentThread,enumerate,activeCount
import time# t = Thread()
# t.start()
# t.join()
# t.is_alive()
# t.isAlive()
# t.ident # 線程標識符 id
# t.daemon# 獲取當前線程對象
# print(currentThread())
# t = Thread(target=lambda :print(currentThread()))
# t.start()t = Thread(target=lambda :time.sleep(1))
t.start()t = Thread(target=lambda :time.sleep(1))
t.start()
t.join()
# 獲取正在運行的所有線程對象 是一個列表
print(enumerate())# 存活的線程數量
print(activeCount())
轉載于:https://www.cnblogs.com/masterjian924/p/11134793.html
總結
以上是生活随笔為你收集整理的Day 33 并发编程3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: day22_面向对象
- 下一篇: day07-字符编码、文件操作