服務端的併發處理-多執行緒多程序
服務端的併發處理-多執行緒多程序
在服務端進行掛起監聽的時候,可能會遇到同時大量的使用者進行連線和資料請求,那麼單程序的可以使用多路複用的方式進行解決這個問題,這個技術後面再講,現在最簡單的解決方案就是使用多執行緒和多程序
多執行緒與多程序的選擇這取決於到底是IO密集型還是CPU密集型,IO密集型需要使用多執行緒,cpu密集型進行多程序
這裡顯然就是io密集型的,但是為了練習,現在使用多執行緒和多程序都實驗一把
from socket import *
from multiprocessing import Process
def main():
# 建立物件
server = socket(AF_INET, SOCK_STREAM)
# 配置斷開釋放埠
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
# 掛起伺服器
server.bind(('', 8888))
# 設定成監聽模式
server.listen()
while True:
# 建立新的連線物件
new_server, client_info = server.accept()
# 建立新的程序
print(f'使用者{client_info}已經連線' )
p = Process(target=get_data, args=(new_server,client_info))
# 啟動程序
p.start()
# 因為記憶體對應的new_server存在兩個指向,所以外面的必須關閉,否則無法釋放掉物件
new_server.close()
def get_data(new_server,client_info):
# 拿出資料
data = new_server.recv(1024)
while data:
# 列印資料
print (f'來自{client_info}的資料{data}')
# 如果一次沒有取完,繼續取
data = new_server.recv(1024)
# 客戶端斷開連線後進行關閉處理
new_server.close()
if __name__ == '__main__':
main()
多程序與多執行緒的區別在於是否進行物件關閉
from socket import *
from threading import Thread
def main():
# 建立物件
server = socket(AF_INET, SOCK_STREAM)
# 配置斷開釋放埠
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
# 掛起伺服器
server.bind(('', 8888))
# 設定成監聽模式
server.listen()
while True:
# 建立新的連線物件
new_server, client_info = server.accept()
# 建立新的程序
print(f'使用者{client_info}已經連線')
t = Thread(target=get_data, args=(new_server,client_info))
# 啟動執行緒
t.start()
# 因為多執行緒並不會複製程序的記憶體,所以new_server的指向不會出現兩個
def get_data(new_server,client_info):
# 拿出資料
data = new_server.recv(1024)
while data:
# 列印資料
print(f'來自{client_info}的資料{data}')
# 如果一次沒有取完,繼續取
data = new_server.recv(1024)
# 客戶端斷開連線後進行關閉處理
new_server.close()
if __name__ == '__main__':
main()
多程序的記憶體複製實質
籠統的認為多程序在開新程序的時候,子程序會複製父程序的記憶體空間,從而具備整整個父程序所具有的變數
這只是非常籠統的認為,實際上計算機記憶體的儲存方式分為兩種一種就是棧,一種是堆
在計算中,如果記憶體地址中的資料沒有變數指向它,則認為整個資料已經不需要了,系統就會對這個資料進行清空以釋放記憶體
實際在我們父程序中建立的物件new_server已經被存在記憶體裡面,父程序儲存的是一個指向這個資料的對映關係,那麼父程序在產生子程序的時候,子程序會複製父程序的這些對映關係,此時new_server物件對映的ip就有兩個變數指向這個記憶體空間,從而使得記憶體中的這個資料ip就會受到兩個指向
上面的進行多程序中,程序函式確實執行完成之後清空的子程序對這個資料ip的指向,子程序的對映關係唄刪除,然而這個資料ip還存在一個父程序的變數進行對映,系統發現記憶體中這個資料ip居然還有一個指向,因此不會進行刪除釋放記憶體
那麼回頭看程式碼,在父程序分裂出子程序後直接繼續執行,父程序馬上就對自己的對映關係進行刪除,此時整個記憶體裡面的資料ip不會消失,因為子程序中還有對這個資料ip的指向
如果子程序也刪除了自己的指向,系統就會知道這個資料ip已經沒有任何指向了,那麼系統就會清空這個資料ip,從而徹底結束這個new_server.
棧是一種先進後出的資料儲存模式,我們大部分變數名就存在這裡面,其優勢在於非常快,然而變數名並不是變數值,棧中儲存的都是一些對映方式
堆則不同,堆用來存大量的複雜資料,其容量大,但是速度慢
比如如果存在
a = 1
b = 1
那麼1這個資料就有兩個變數進行指向,所以資料1的對映數為2,當我們執行a += 1,此時變數名a指向的是2,而2是一個新的資料,在記憶體中會分配一個新的地址,此時資料1的對映數就減1稱為了1.系統之所以沒有清空資料1佔用的記憶體,因為b這個變數對映在資料1上面還有對映
敲黑板
如果此時對b進行 b += 1的操作,則此時就會執行,先將資料2上面增加一個對映關係,這個對映是b變數指向的,此時資料2存在的對映數為2,那麼原來的資料1就會減少一個對映關係,此時的資料1上的對映數為0,系統此時發現數據1上已經沒有映射了,那麼就會將資料0佔用的記憶體釋放掉,那麼ab兩個變數再次指向的是同一片記憶體空間