天天看點

服務端的并發處理-多線程多程序服務端的并發處理-多線程多程序多程序的記憶體複制實質

服務端的并發處理-多線程多程序

在服務端進行挂起監聽的時候,可能會遇到同時大量的使用者進行連接配接和資料請求,那麼單程序的可以使用多路複用的方式進行解決這個問題,這個技術後面再講,現在最簡單的解決方案就是使用多線程和多程序

多線程與多程序的選擇這取決于到底是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兩個變量再次指向的是同一片記憶體空間