<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
為了更好地瞭解IO模型,我們需要事先回顧下:同步、非同步、阻塞、非阻塞
五種I/O模型包括:阻塞I/O、非阻塞I/O、訊號驅動I/O(不常用)、I/O多路轉接、非同步I/O。其中,前四個被稱為同步I/O。
上五個模型的阻塞程度由低到高為:阻塞I/O > 非阻塞I/O > 多路轉接I/O > 訊號驅動I/O > 非同步I/O,因此他們的效率是由低到高的。
在linux中,預設情況下所有的socket都是blocking,除非特別指定,幾乎所有的I/O介面 ( 包括socket介面 ) 都是阻塞型的。
如果所面臨的可能同時出現的上千甚至上萬次的使用者端請求,“執行緒池”或“連線池”或許可以緩解部分壓力,但是不能解決所有問題。總之,多執行緒模型可以方便高效的解決小規模的服務請求,但面對大規模的服務請求,多執行緒模型也會遇到瓶頸,可以用非阻塞介面來嘗試解決這個問題。
在非阻塞式I/O中,使用者程序其實是需要不斷的主動詢問kernel資料準備好了沒有。但是非阻塞I/O模型絕不被推薦。
非阻塞,不等待。比如建立socket對某個地址進行connect、獲取接收資料recv時預設都會等待(連線成功或接收到資料),才執行後續操作。
如果設定setblocking(False),以上兩個過程就不再等待,但是會報BlockingIOError的錯誤,只要捕獲即可。
非同步,通知,執行完成之後自動執行回撥函數或自動執行某些操作(通知)。比如做爬蟲中向某個地址baidu。com傳送請求,當請求執行完成之後自執行回撥函數。
基於事件迴圈的非同步非阻塞框架:如Twisted框架,scrapy框架(單執行緒完成並行)。
檢測多個socket是否已經發生變化(是否已經連線成功/是否已經獲取資料)(可讀/可寫)IO多路複用作用?
作業系統檢測socket是否發生變化,有三種模式:
Python模組:
基於IO多路複用+socket非阻塞,實現並行請求(一個執行緒100個請求)
import socket # 建立socket client = socket.socket() # 將原來阻塞的位置變成非阻塞(報錯) client.setblocking(False) # 百度建立連線: 阻塞 try: # 執行了但報錯了 client.connect(('www.baidu.com',80)) except BlockingIOError as e: pass # 檢測到已經連線成功 # 問百度我要什麼? client.sendall(b'GET /s?wd=alex HTTP/1.0rnhost:www.baidu.comrnrn') # 我等著接收百度給我的回覆 chunk_list = [] while True: # 將原來阻塞的位置變成非阻塞(報錯) chunk = client.recv(8096) if not chunk: break chunk_list.append(chunk) body = b''.join(chunk_list) print(body.decode('utf-8'))
#伺服器端 from socket import * import selectors sel=selectors.DefaultSelector() def accept(server_fileobj,mask): conn,addr=server_fileobj.accept() sel.register(conn,selectors.EVENT_READ,read) def read(conn,mask): try: data=conn.recv(1024) if not data: print('closing',conn) sel.unregister(conn) conn.close() return conn.send(data.upper()+b'_SB') except Exception: print('closing', conn) sel.unregister(conn) conn.close() server_fileobj=socket(AF_INET,SOCK_STREAM) server_fileobj.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server_fileobj.bind(('127.0.0.1',8088)) server_fileobj.listen(5) server_fileobj.setblocking(False) #設定socket的介面為非阻塞 sel.register(server_fileobj,selectors.EVENT_READ,accept) #相當於網select的讀列表裡append了一個檔案控制程式碼 #server_fileobj,並且繫結了一個回撥函數accept while True: events=sel.select() #檢測所有的fileobj,是否有完成wait data的 for sel_obj,mask in events: callback=sel_obj.data #callback=accpet callback(sel_obj.fileobj,mask) #accpet(server_fileobj,1) #使用者端 from socket import * c=socket(AF_INET,SOCK_STREAM) c.connect(('127.0.0.1',8088)) while True: msg=input('>>: ') if not msg:continue c.send(msg.encode('utf-8')) data=c.recv(1024) print(data.decode('utf-8'))
asyncio
是Python 3.4版本引入的標準庫,直接內建了對非同步IO的支援。
asyncio
的程式設計模型就是一個訊息迴圈。我們從asyncio
模組中直接獲取一個EventLoop
的參照,然後把需要執行的協程扔到EventLoop
中執行,就實現了非同步IO。
用asyncio
實現Hello world
程式碼如下:
import asyncio @asyncio.coroutine def hello(): print("Hello world!") # 非同步呼叫asyncio.sleep(1): r = yield from asyncio.sleep(1) print("Hello again!") # 獲取EventLoop: loop = asyncio.get_event_loop() # 執行coroutine loop.run_until_complete(hello()) loop.close()
@asyncio.coroutine
把一個generator標記為coroutine型別,然後,我們就把這個coroutine
扔到EventLoop
中執行。
hello()
會首先列印出Hello world!
,然後,yield from
語法可以讓我們方便地呼叫另一個generator
。由於asyncio.sleep()
也是一個coroutine
,所以執行緒不會等待asyncio.sleep()
,而是直接中斷並執行下一個訊息迴圈。當asyncio.sleep()
返回時,執行緒就可以從yield from
拿到返回值(此處是None
),然後接著執行下一行語句。
把asyncio.sleep(1)
看成是一個耗時1秒的IO操作,在此期間,主執行緒並未等待,而是去執行EventLoop
中其他可以執行的coroutine
了,因此可以實現並行執行。
我們用Task封裝兩個coroutine
試試:
import threading import asyncio @asyncio.coroutine def hello(): print('Hello world! (%s)' % threading.currentThread()) yield from asyncio.sleep(1) print('Hello again! (%s)' % threading.currentThread()) loop = asyncio.get_event_loop() tasks = [hello(), hello()] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
觀察執行過程:
Hello world! (<_MainThread(MainThread, started 140735195337472)>) Hello world! (<_MainThread(MainThread, started 140735195337472)>) (暫停約1秒) Hello again! (<_MainThread(MainThread, started 140735195337472)>) Hello again! (<_MainThread(MainThread, started 140735195337472)>)
由列印的當前執行緒名稱可以看出,兩個coroutine
是由同一個執行緒並行執行的。
如果把asyncio.sleep()
換成真正的IO操作,則多個coroutine
就可以由一個執行緒並行執行。
我們用asyncio
的非同步網路連線來獲取sina、sohu和163的網站首頁:
import asyncio @asyncio.coroutine def wget(host): print('wget %s...' % host) connect = asyncio.open_connection(host, 80) reader, writer = yield from connect header = 'GET / HTTP/1.0rnHost: %srnrn' % host writer.write(header.encode('utf-8')) yield from writer.drain() while True: line = yield from reader.readline() if line == b'rn': break print('%s header > %s' % (host, line.decode('utf-8').rstrip())) # Ignore the body, close the socket writer.close() loop = asyncio.get_event_loop() tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
執行結果如下:
wget www.sohu.com... wget www.sina.com.cn... wget www.163.com... (等待一段時間) (列印出sohu的header) www.sohu.com header > HTTP/1.1 200 OK www.sohu.com header > Content-Type: text/html ... (列印出sina的header) www.sina.com.cn header > HTTP/1.1 200 OK www.sina.com.cn header > Date: Wed, 20 May 2015 04:56:33 GMT ... (列印出163的header) www.163.com header > HTTP/1.0 302 Moved Temporarily www.163.com header > Server: Cdn Cache Server V2.0 ...
可見3個連線由一個執行緒通過coroutine
並行完成。
用asyncio
提供的@asyncio.coroutine
可以把一個generator標記為coroutine型別,然後在coroutine內部用yield from
呼叫另一個coroutine實現非同步操作。
為了簡化並更好地標識非同步IO,從Python 3.5開始引入了新的語法async
和await
,可以讓coroutine的程式碼更簡潔易讀。
請注意,async
和await
是針對coroutine的新語法,要使用新的語法,只需要做兩步簡單的替換:
@asyncio.coroutine
替換為async
;yield from
替換為await
。讓我們對比一下上一節的程式碼:
@asyncio.coroutine def hello(): print("Hello world!") r = yield from asyncio.sleep(1) print("Hello again!")
用新語法重新編寫如下:
async def hello(): print("Hello world!") r = await asyncio.sleep(1) print("Hello again!")
剩下的程式碼保持不變。
asyncio
提供了完善的非同步IO支援;
非同步操作需要在coroutine
中通過yield from
完成;
多個coroutine
可以封裝成一組Task然後並行執行。
到此這篇關於Python並行程式設計之IO模型的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45