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