摘要:最重要的是每個(gè)線程,對應(yīng)了一個(gè)函數(shù)的執(zhí)行。有多個(gè)線程同時(shí)執(zhí)行的時(shí)候,每個(gè)線程的狀態(tài)是由操作系統(tǒng)內(nèi)核負(fù)責(zé)保存在內(nèi)存中的。在多線程的實(shí)現(xiàn)中。并且內(nèi)核的線程在切換多個(gè)線程的時(shí)候,線程切換的開銷是比較大。
上次的網(wǎng)絡(luò)編程的例子,改寫成多線程的是這樣:
import socket import thread def main(): 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) while True: conn_sock, client_addr = listen_sock.accept() thread.start_new(serve, (conn_sock, client_addr)) def serve(conn_sock, client_addr): print("connected from %s:%s" % client_addr) input = conn_sock.recv(8192) while "done" != input.strip(): conn_sock.sendall(input) input = conn_sock.recv(8192) conn_sock.sendall("bye! ") conn_sock.close() main()
變成多線程之后,就可以有多個(gè)客戶端同時(shí)連接到服務(wù)器并同時(shí)進(jìn)行服務(wù)了。最重要的是每個(gè)線程,對應(yīng)了一個(gè)“serve”函數(shù)的執(zhí)行。所以函數(shù)執(zhí)行就是有一個(gè)函數(shù)的棧,棧上有一個(gè)函數(shù)的參數(shù)和局部變量。最重要的一個(gè)局部變量就是conn_sock,有了這個(gè)socket就可以和對應(yīng)的客戶端進(jìn)行對話。
機(jī)器都有一個(gè)ESP的寄存器指向函數(shù)的棧頂所在的內(nèi)存地址。一個(gè)cpu核只有一個(gè)ESP寄存器。有多個(gè)線程同時(shí)執(zhí)行的時(shí)候,每個(gè)線程的狀態(tài)是由操作系統(tǒng)內(nèi)核負(fù)責(zé)保存在內(nèi)存中的。當(dāng)這個(gè)線程被調(diào)度為執(zhí)行狀態(tài)的時(shí)候,ESP寄存器被切換為當(dāng)前線程的棧頂位置,然后繼續(xù)執(zhí)行這個(gè)線程的后面的指令。服務(wù)器可以支持多個(gè)客戶端,就有兩個(gè)關(guān)鍵的問題要解決:
保存每個(gè)客戶端的服務(wù)狀態(tài)(最起碼要保存對應(yīng)這個(gè)客戶端的socket)
一個(gè)全局scheduler來負(fù)責(zé)I/O,在需要的時(shí)候把客戶端對應(yīng)的狀態(tài)切換為“當(dāng)前活躍”狀態(tài)。在線程調(diào)用了阻塞的I/O操作時(shí),操作系統(tǒng)內(nèi)核就把線程給掛起了,同時(shí)在映射表里記錄一個(gè)對應(yīng)關(guān)系,哪個(gè)I/O阻塞的fd對應(yīng)的是哪個(gè)線程在等待它。等I/O阻塞條件滿足了,對應(yīng)的線程就會被查表得到然后被喚醒。
在多線程的實(shí)現(xiàn)中。每個(gè)客戶端的狀態(tài)就是保存在線程對應(yīng)函數(shù)的棧上的,而全局的scheduler就是內(nèi)核的線程scheduler。這種實(shí)現(xiàn)方式最大的缺點(diǎn)是線程的棧是創(chuàng)建時(shí)預(yù)先分配的很大的一塊區(qū)域,大量線程會耗費(fèi)過度內(nèi)存。并且內(nèi)核的線程scheduler在切換多個(gè)線程的時(shí)候,線程切換的開銷是比較大。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/45305.html
摘要:前面的網(wǎng)絡(luò)編程的例子使用多進(jìn)程也是可以實(shí)現(xiàn)的其中之后會創(chuàng)建一個(gè)子進(jìn)程。從效率上來說,具有多線程一樣的問題,而且內(nèi)存占用會更高,切換成本也更高。多線程和多進(jìn)程的版本從代碼可讀性上來說還是非常不錯(cuò)的,很好懂,從上至下平鋪直敘的。 前面的網(wǎng)絡(luò)編程的例子使用多進(jìn)程也是可以實(shí)現(xiàn)的: import socket import os def main(): listen_sock = s...
摘要:簡單來說就是一個(gè)操作系統(tǒng)提供的回調(diào)機(jī)制。其中這一步是創(chuàng)建,是做一個(gè)調(diào)用,后面的是輪詢,這一步是根據(jù)返回的查找對應(yīng)的回調(diào)函數(shù)回調(diào)。這樣狀態(tài)從多個(gè)線程的多個(gè)棧上,變成了只有一個(gè)線程,但是在線程內(nèi)部有一個(gè)來維護(hù)單線程內(nèi)多個(gè)并發(fā)流程的狀態(tài)。 為了讓I/O阻塞的時(shí)候,程序還可以去干別的。除了使用線程模型,讓操作系統(tǒng)的內(nèi)核去調(diào)度多個(gè)線程,Windows提供了IOCP機(jī)制。簡單來說就是一個(gè)操作系統(tǒng)提...
摘要:在了解了的實(shí)現(xiàn)方式的基礎(chǔ)之上,希望能夠把流程阻塞的功能在的框架之上實(shí)現(xiàn),從而可以制作一個(gè)簡單的類似,這樣的集群調(diào)度工具。我們先來看一個(gè)最基本的網(wǎng)絡(luò)編程的例子這是一個(gè)服務(wù)器。 接下來,會把Python tulip這個(gè)網(wǎng)絡(luò)庫(也就是3.4之后的asyncio)如何實(shí)現(xiàn)的進(jìn)行一些分析。在了解了tulip的實(shí)現(xiàn)方式的基礎(chǔ)之上,希望能夠把流程阻塞的功能在tulip的框架之上實(shí)現(xiàn),從而可以制作一個(gè)...
摘要:是之后引入的標(biāo)準(zhǔn)庫的,這個(gè)包使用事件循環(huán)驅(qū)動的協(xié)程實(shí)現(xiàn)并發(fā)。沒有能從外部終止線程,因?yàn)榫€程隨時(shí)可能被中斷。上一篇并發(fā)使用處理并發(fā)我們介紹過的,在中,只是調(diào)度執(zhí)行某物的結(jié)果。 asyncio asyncio 是Python3.4 之后引入的標(biāo)準(zhǔn)庫的,這個(gè)包使用事件循環(huán)驅(qū)動的協(xié)程實(shí)現(xiàn)并發(fā)。asyncio 包在引入標(biāo)準(zhǔn)庫之前代號 Tulip(郁金香),所以在網(wǎng)上搜索資料時(shí),會經(jīng)??吹竭@種花的...
摘要:我們可以看一下的可見是由內(nèi)部支持的,其實(shí)現(xiàn)原理上就避免了棧進(jìn)棧出的消耗,直接由最內(nèi)層的返回值。另外可以實(shí)現(xiàn)外部直接向最內(nèi)層的傳遞值,比如這段代碼的輸出是這樣傳值的方式,在用循環(huán)重新的模式下是無法實(shí)現(xiàn)的。這也就是必須使用,而不能使用的原因。 在python 3.3里,generator新增了一個(gè)語法 yield from 這個(gè)yield from的作用是什么?看下面兩段對比的代碼: d...
閱讀 1276·2021-10-18 13:32
閱讀 2355·2021-09-24 09:47
閱讀 1336·2021-09-23 11:22
閱讀 2473·2019-08-30 14:06
閱讀 579·2019-08-30 12:48
閱讀 2010·2019-08-30 11:03
閱讀 546·2019-08-29 17:09
閱讀 2473·2019-08-29 14:10