异步同步、阻塞非阻塞、异步回调、线程队列和协程
今天學習了異步同步、阻塞非阻塞、異步回調、線程隊列和協程
一、異步同步和阻塞非阻塞
線程的三種狀態:
1、就緒
2、運行
3、阻塞
阻塞:遇到了IO操作 ?代碼卡住 ?無法執行下一行 ?CPU會切換到其他任務
非阻塞: 與阻塞相反 代碼正在執行(運行狀態) 或處于就緒狀態
阻塞和非阻塞描述的是運行的狀態
同步:提交任務必須等待任務完成,才能執行下一行
異步:提交任務不需要等待任務完成,立即執行下一行
指的是一種提交任務的方式
二、異步回調
為什么回調:子進程幫助主進程完成任務 處理任務的結果應該交還給主進程
其他方式也可以將數據交還給主進程
1、shutdown ?主進程會等到所有任務完成
2、result函數 ?會阻塞直到任務完成
注意:
回調函數什么時候被執行?子進程完成時
誰在執行回調函數?主進程
線程的異步回調
使用方式都相同 ?唯一的不同是執行回調函數 是子線程在執行
#進程利用回調完成生產者消費者from concurrent.futures import ProcessPoolExecutor import os pool = ProcessPoolExecutor()#爬蟲 從網絡某個地址獲取一個HTML文件 import requests #該模塊用于網絡請求 #生產數據 def get_data_task(url):print(os.getpid(),'正在生產數據!')response = requests.get(url)text = response.content.decode('utf-8')return text#處理數據 def parser_data(f):print(os.getpid(),'處理數據')print('正在解析:長度%s'%len(f.result()))urls = [ 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com' ]if __name__ == '__main__':for url in urls:f = pool.submit(get_data_task,url)f.add_done_callback(parser_data) #回調函數是主進程在執行#因為子進程是負責獲取數據的 然而數據怎么處理 子進程并不知道 應該把數據還給主進程print('over') #線程利用回調完成生產者消費者 from concurrent.futures import ThreadPoolExecutor from threading import current_threadpool = ThreadPoolExecutor #爬蟲 從網絡某個地址獲取一個HTML文件 import requests #該模塊用于網絡(HTTP)請求 #生產數據 def get_data_task(url):print(current_thread(),'正在生產數據!')response = requests.get(url)text = response.content.decode('utf-8')return text#處理數據 def parser_data(f):print(current_thread(),'處理數據')print('正在解析:長度%s'%len(f.result()))urls = [ 'http://www.baidu.com', 'http://www,baidu.com', 'http://www.baidu.com', 'http://www.baidu.com' ]if __name__ =='__main__':for url in urls:f = pool.submit(get_data_task,url)f.add_done_callback(parser_data) #因為是子線程在執行回調函數 所以沒有主次之分 任何子線程都可以對函數進行回調print('over')
三、線程隊列
import queue #普通隊列 先進先出 q = queue.Queue() q.put('a') q.put('b') print(q.get()) print(q.get())#堆棧隊列 先進后出 函數調用就是進棧 函數結束就出棧 遞歸造成棧溢出 q2 = queue.LifoQueue() q2.put('a') q2.put('b') print('q2.get()')#優先級隊列 q3 = queue.PriorityQueue() #數值越小優先級越高 優先級相同時 比較大小 小的先取 q3.put((-100,'c')) q3.put((1,'a')) q3.put((100,b)) print(q3.get())
四、協程
協程的目的是在單線程下實現并發
單線程下實現并發 將io阻塞時間用于執行計算 可以提高效率 原理:一直使用CPU直到超時
怎么實現單線程并發?
并發 ?指的是 ?看起來像是同時運行 實際是在任務間來回切換 同時需要保存執行的狀態
任務是一堆代碼 可以用函數裝起來
1.如何讓兩個函數切換執行
yield可以保存函數的執行狀態
通過生成器可以實現偽并發
并發不一定提升效率 ?反而會降低效率 當任務全是計算時
2.如何知道發生了io?從而切換執行
目前咱們實現不了。。
第三方模塊 greenlet 可以實現并發 但是不能檢測io
第三方模塊 gevent 封裝greenlet 可以實現單線程并發 并且能夠檢測io操作 自動切換
#用yield實現兩個函數切換執行 import time def task():while True:print('task1')time.sleep(4)yield 1def task2():g = task()while True:try:print('task2')next(g)except Exception:print('任務完成')break task2() #使用greenlet模塊實現并發 import greenlet import time def task1():print('task1 1')time.sleep(2)g2.switch()print('task1 2')g2.swith()def task2():print('task2 1')g1.switch()print('task2 2')g1 = greenlet.greenlet(task1) g2 = greenlet.greenlte(task2) g1.switch() #1.實例化greenlet得到一個對象 傳入要執行的任務 #2.先讓某個任務執行起來 使用對象調用switch #3.在任務的執行過程中 手動調用switch來切換 #使用gevent模塊實現單線程的并發 from gevent import monkey monkey.patch_all() import gevent import time def eat():print('eat food 1')time.sleep(2)print('eat food 2')def play():print('play 1')time.sleep(1)print('play 2')g1 = gevent.spawn(eat) g2 = gevent.spawn(play) gevent.joinall([g1,g2]) print('主')#1.spawn函數傳入你的任務 #2.調用join 去開啟任務 #3.檢測io操作需要打mokey補丁 就是一個函數 在程序最開始的地方調用它
?
轉載于:https://www.cnblogs.com/xiaocaiyang/p/9954077.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的异步同步、阻塞非阻塞、异步回调、线程队列和协程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sklearn.feature_extr
- 下一篇: CSU2188: Substring