服務端的并發處理-多線程多程序
在服務端進行挂起監聽的時候,可能會遇到同時大量的使用者進行連接配接和資料請求,那麼單程序的可以使用多路複用的方式進行解決這個問題,這個技術後面再講,現在最簡單的解決方案就是使用多線程和多程序
多線程與多程序的選擇這取決于到底是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兩個變量再次指向的是同一片記憶體空間