日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Python:通过一个小案例深入理解IO多路复用

發布時間:2023/11/27 生活经验 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python:通过一个小案例深入理解IO多路复用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過一個小案例深入理解IO多路復用

假如我們現在有這樣一個普通的需求,寫一個簡單的爬蟲來爬取?;ňW的主頁

import requests
import timestart = time.time()url = 'http://www.xiaohuar.com/'
result = requests.get(url).textprint(result)
print(time.time()-start)

  

  這樣子是顯然沒啥問題的,總共耗時約為6秒

?

但是有沒有辦法更進一步優化呢,這里如果需要優化我們首先需要知道一個知識點

就是requests這個模塊它底層其實是封裝了urllib2和urllib3的,而這兩個模塊底層其實就是socket

如果需要優化,從requests是實現不了的,那么能不能從socket來呢

如果從socket,又該如何優化呢?

?

首先我們得知道socket到底做了什么,

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
url = 'www.xiaohuar.com/'
client.connect((url, 80))
client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))
data = b''
while 1:d = client.recv(1024)if d:data +=delse:breakprint(data)

  這里的代碼就是上面那個requests版本的代碼的底層

  在這一坨代碼中,有幾個點需要注意

  connect和recv,這兩個方法都是阻塞io,也就是說,如果連接不到或者接受不到消息的話,程序就會一直等,等到預期的效果為止。

  這就是阻塞

?

  阻塞有個很大的弊端,那就是cpu無法得到充分利用,因為等待的時間里,cpu是空閑的,而我們又沒有執行其他的操作,那么這段時間我們能不能充分利用起來呢

  答案是肯定的,socket提供了一個非阻塞的辦法

  

client.setblocking(False)

  直接運行試試效果

  BlockingIOError: [WinError 10035] 無法立即完成一個非阻止性套接字操作。

  結果是拋出了這個異常,這是因為當變為非阻塞時候,連接?;ňW的url的時候,三次握手還沒建立完成,我們就去執行下一步了

  

try:client.connect((url, 80))
except BlockingIOError as e:
  #處理其他事情pass

  那么我們可以這樣改,抓到這個異常但是不處理,這樣子,我們就能在except后面加入其他的代碼了,也就是說cpu發個請求就不管了,然后去執行后面的代碼,這樣效率就提高了。

  再運行一次。

  OSError: [WinError 10057] 由于套接字沒有連接并且(當使用一個 sendto 調用發送數據報套接字時)沒有提供地址,發送或接收數據的請求沒有被接受。

  又拋出了一個異常,和上面的原理差不多,因為是非阻塞模式

最終代碼如下

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setblocking(False)
url = 'www.xiaohuar.com'
try:client.connect((url, 80))
except BlockingIOError as e:passwhile 1:try:client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))breakexcept Exception as e:passdata = b''
while 1:try:d = client.recv(1024)except Exception as e:continueif d:data += delse:breakprint(data)

  這樣子雖然有一段時間更充分利用了cpu 但是代碼很亂,很麻煩,其次雖然是非阻塞,但是有兩個地方只是把之前的阻塞的時間花費了在循環上,那么有沒有更好的辦法呢?

?

這里就要引入IO多路復用的概念了

IO復用就是通過一種機制,一個進程可以監視多個描述符,一旦某個描述符就緒(讀或者寫),都能夠通知程序來進行相應的讀寫操作,但是select,poll和epoll都是同步io,也就是說這個讀寫過程是阻塞的,而異步io則無需自己進行讀寫,異步io的實現會負責把數據從內核拷貝到用戶內存。

?

select在windows,OS X, 或者linux都能用,但是select最大監視數量只能為1024

而poll的話其他幾乎與select一樣,只是突破了最大限制

而epoll就與前面這兩個都不一樣了,它底層使用了紅黑樹的數據結構,epoll使用一個文件描述符來管理多個文件描述符,將用戶關系的文件描述符的事件存放到內核的一個事件表之中,這樣在用戶空間和內核空間的copy只需一次。

而poll和select都是才用輪詢的方式,所以效率差就在這里體現出來了

?

最終代碼 異步IO

from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
import socketselector = DefaultSelector()class Fetcher():def send_msg(self, key):selector.unregister(key.fd)self.client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/', 80).encode('utf8'))selector.register(self.client.fileno(), EVENT_READ, self.recv)def recv(self, key):d = self.client.recv(1024)if d:self.data += delse:selector.unregister(key.fd)print(self.data.decode('utf8'))def get_url(self, url):self.data = b''try:self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client.connect((url, 80))except Exception as e:# 加入另外的邏輯passselector.register(self.client.fileno(), EVENT_WRITE, self.send_msg)def loop_forever():while 1:ready = selector.select()for key, mask in ready:call_back = key.datacall_back(key)if __name__ == '__main__':fet = Fetcher()fet.get_url('www.xiaohuar.com')loop_forever()

  

?

轉載于:https://www.cnblogs.com/Miracle-boy/p/10004684.html

總結

以上是生活随笔為你收集整理的Python:通过一个小案例深入理解IO多路复用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。