摘要:簡單來說就是一個操作系統提供的回調機制。其中這一步是創建,是做一個調用,后面的是輪詢,這一步是根據返回的查找對應的回調函數回調。這樣狀態從多個線程的多個棧上,變成了只有一個線程,但是在線程內部有一個來維護單線程內多個并發流程的狀態。
為了讓I/O阻塞的時候,程序還可以去干別的。除了使用線程模型,讓操作系統的內核去調度多個線程,Windows提供了IOCP機制。簡單來說就是一個操作系統提供的回調機制。分成四個步驟
生成key,并建立映射關系:向操作系統創建一個key,程序內部把這個key和一個回調函數對應起來
調用:執行阻塞的I/O操作,并指定key來對應這個I/O操作
輪詢,返回key:程序輪詢操作系統詢問是否有新的I/O操作完成,如果有完成的會返回對應的key
用key查找,并回調:因為創建key的時候內部已經和一個回調函數對應起來了,所以這個時候之前映射好的函數會被回調
前面的例子太復雜了,我們把accept后面的操作全部忽略掉。多帶帶看一個服務器接收客戶端連接的代碼:
import socket from asyncio import _overlapped import struct listen_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_IP) listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listen_sock.bind(("0.0.0.0", 9090)) listen_sock.listen(0) NULL = 0 concurrency=0xffffffff _iocp = _overlapped.CreateIoCompletionPort(_overlapped.INVALID_HANDLE_VALUE, NULL, 0, concurrency) _overlapped.CreateIoCompletionPort(listen_sock.fileno(), _iocp, 0, 0) conn_sock = socket.socket(listen_sock.family) conn_sock.settimeout(0) ov = _overlapped.Overlapped(NULL) ov.AcceptEx(listen_sock.fileno(), conn_sock.fileno()) def on_accepted(): buf = struct.pack("@P", listen_sock.fileno()) conn_sock.setsockopt(socket.SOL_SOCKET, _overlapped.SO_UPDATE_ACCEPT_CONTEXT, buf) conn_sock.settimeout(listen_sock.gettimeout()) print("connected from %s:%s" % conn_sock.getpeername()) return conn_sock, conn_sock.getpeername() callback_map = {} if ov.pending: callback_map[ov.address] = on_accepted else: on_accepted() while True: # wait maximum 1 second status = _overlapped.GetQueuedCompletionStatus(_iocp, 1000) if status is None: continue # try again err, transferred, key, address = status callback = callback_map[address] callback() break
這段代碼使用了Python 3.4。其中 _overlapped.Overlapped(NULL) 這一步是創建key,ov.AcceptEx(listen_sock.fileno(), conn_sock.fileno()) 是做一個I/O調用,后面的 _overlapped.GetQueuedCompletionStatus(_iocp, 1000) 是輪詢,callback_map[address] 這一步是根據返回的key查找對應的回調函數回調。
這種實現方式與前面基于線程的方式顯著不同:
程序內狀態的上下文的保存不再由操作系統負責,而是通過callback_map由程序代碼自己來負責的
操作系統只負責維護阻塞I/O操作與對應的key(也就是overlapped.address這個東西)的關系。程序內的多個并發流程(本例子里只有一個客戶端)需要由程序自身通過key和callback_map來自己做調度。
這樣狀態從多個線程的多個棧上,變成了只有一個線程,但是在線程內部有一個callback_map來維護單線程內多個并發流程的狀態。某種程度上來說,相對于多線程是把一些操作系統的上下文保存和調度職責從操作系統內核移到了網絡程序里。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/45303.html
摘要:在了解了的實現方式的基礎之上,希望能夠把流程阻塞的功能在的框架之上實現,從而可以制作一個簡單的類似,這樣的集群調度工具。我們先來看一個最基本的網絡編程的例子這是一個服務器。 接下來,會把Python tulip這個網絡庫(也就是3.4之后的asyncio)如何實現的進行一些分析。在了解了tulip的實現方式的基礎之上,希望能夠把流程阻塞的功能在tulip的框架之上實現,從而可以制作一個...
摘要:前面的網絡編程的例子使用多進程也是可以實現的其中之后會創建一個子進程。從效率上來說,具有多線程一樣的問題,而且內存占用會更高,切換成本也更高。多線程和多進程的版本從代碼可讀性上來說還是非常不錯的,很好懂,從上至下平鋪直敘的。 前面的網絡編程的例子使用多進程也是可以實現的: import socket import os def main(): listen_sock = s...
摘要:最重要的是每個線程,對應了一個函數的執行。有多個線程同時執行的時候,每個線程的狀態是由操作系統內核負責保存在內存中的。在多線程的實現中。并且內核的線程在切換多個線程的時候,線程切換的開銷是比較大。 上次的網絡編程的例子,改寫成多線程的是這樣: import socket import thread def main(): listen_sock = socket.socke...
下表比較了Gruvi針對asyncio,gevent和eventlet的一些設計決策和功能。 * 特征 Gruvi Asyncio Gevent Eventlet IO library(依賴包) libuv stdlib libev stdlib /?libevent IO abstractionTransports/Protocols Transports/ProtocolsGre...
閱讀 2867·2021-10-08 10:12
閱讀 3966·2021-09-22 15:45
閱讀 2555·2019-08-30 15:52
閱讀 2625·2019-08-29 18:44
閱讀 2644·2019-08-29 12:37
閱讀 1154·2019-08-26 13:36
閱讀 2561·2019-08-26 13:34
閱讀 1473·2019-08-26 12:20