網絡編程之併發編程——互斥鎖

網絡編程之併發編程——互斥鎖

1、互斥鎖

進程之間數據不共享,可是共享同一套文件系統,因此訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結果就是錯亂,以下:python

#併發運行,效率高,但競爭同一打印終端,帶來了打印錯亂
from multiprocessing import Process
import os,time
def work():
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())
if __name__ == '__main__':
    for i in range(3):
        p=Process(target=work)
        p.start()

如何控制,就是加鎖處理。互斥鎖的意思就是互相排斥,若是把多個進程比喻爲多我的,互斥鎖的工做原理就是多我的都要去爭搶同一個資源:衛生間。一我的搶到衛生間後上一把鎖,其它人都要等着,等到這個任務完成後釋放鎖,其餘人才有可能有一個搶到。因此互斥鎖的原理,就是把併發改爲串行,下降了效率,但保證了數據俺去那不錯亂。數據庫

#由併發變成了串行,犧牲了運行效率,但避免了競爭
from multiprocessing import Process,Lock
import os,time
def work(lock):
    lock.acquire() #加鎖
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())
    lock.release() #釋放鎖
if __name__ == '__main__':
    lock=Lock()
    for i in range(3):
        p=Process(target=work,args=(lock,))
        p.start()

2、模擬搶票練習

多個進程共享同一個文件,咱們能夠把文件當數據庫,用多個進程模擬多我的執行搶票任務。編程

#文件db.txt的內容爲:{"count":1}
#注意必定要用雙引號,否則json沒法識別
from multiprocessing import Process
import time,json
def search(name):
    dic=json.load(open('db.txt'))
    time.sleep(1)
    print('\033[43m%s 查到剩餘票數%s\033[0m' %(name,dic['count']))
def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模擬讀數據的網絡延遲
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模擬寫數據的網絡延遲
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 購票成功\033[0m' %name)
def task(name):
    search(name)
    get(name)
if __name__ == '__main__':
    for i in range(10): #模擬併發10個客戶端搶票
        name='<路人%s>' %i
        p=Process(target=task,args=(name,))
        p.start()

併發運行,效率高,但競爭寫同一文件,數據寫入錯亂,只有一張票,賣成功給了10我的。json

<路人0> 查到剩餘票數1
<路人1> 查到剩餘票數1
<路人2> 查到剩餘票數1
<路人3> 查到剩餘票數1
<路人4> 查到剩餘票數1
<路人5> 查到剩餘票數1
<路人6> 查到剩餘票數1
<路人7> 查到剩餘票數1
<路人8> 查到剩餘票數1
<路人9> 查到剩餘票數1
<路人0> 購票成功
<路人4> 購票成功
<路人1> 購票成功
<路人5> 購票成功
<路人3> 購票成功
<路人7> 購票成功
<路人2> 購票成功
<路人6> 購票成功
<路人8> 購票成功
<路人9> 購票成功

加鎖處理:購票行爲由併發變成了串行,犧牲了運行效率,但保證了數據安全。安全

#把文件db.txt的內容重置爲:{"count":1}
from multiprocessing import Process,Lock
import time,json
def search(name):
    dic=json.load(open('db.txt'))
    time.sleep(1)
    print('\033[43m%s 查到剩餘票數%s\033[0m' %(name,dic['count']))
def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模擬讀數據的網絡延遲
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模擬寫數據的網絡延遲
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 購票成功\033[0m' %name)
def task(name,lock):
    search(name)
    with lock: #至關於lock.acquire(),執行完自代碼塊自動執行lock.release()
        get(name)
if __name__ == '__main__':
    lock=Lock()
    for i in range(10): #模擬併發10個客戶端搶票
        name='<路人%s>' %i
        p=Process(target=task,args=(name,lock))
        p.start()

執行結果:網絡

<路人0> 查到剩餘票數1
<路人1> 查到剩餘票數1
<路人2> 查到剩餘票數1
<路人3> 查到剩餘票數1
<路人4> 查到剩餘票數1
<路人5> 查到剩餘票數1
<路人6> 查到剩餘票數1
<路人7> 查到剩餘票數1
<路人8> 查到剩餘票數1
<路人9> 查到剩餘票數1
<路人0> 購票成功

3、互斥鎖與join

使用join能夠將併發變成串行,互斥鎖的原理也是將併發變成串行,那咱們直接使用join就能夠了,爲什麼還要用互斥鎖?併發

把文件db.txt的內容重置爲:{「count」:1}函數

from multiprocessing import Process,Lock
import time,json
def search(name):
    dic=json.load(open('db.txt'))
    print('\033[43m%s 查到剩餘票數%s\033[0m' %(name,dic['count']))
def get(name):
    dic=json.load(open('db.txt'))
    time.sleep(1) #模擬讀數據的網絡延遲
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(1) #模擬寫數據的網絡延遲
        json.dump(dic,open('db.txt','w'))
        print('\033[46m%s 購票成功\033[0m' %name)
def task(name,):
    search(name)
    get(name)
if __name__ == '__main__':
    for i in range(10):
        name='<路人%s>' %i
        p=Process(target=task,args=(name,))
        p.start()
        p.join()

執行結果:ui

<路人0> 查到剩餘票數1
<路人0> 購票成功
<路人1> 查到剩餘票數0
<路人2> 查到剩餘票數0
<路人3> 查到剩餘票數0
<路人4> 查到剩餘票數0
<路人5> 查到剩餘票數0
<路人6> 查到剩餘票數0
<路人7> 查到剩餘票數0
<路人8> 查到剩餘票數0
<路人9> 查到剩餘票數0

發現使用join將併發改爲穿行,確實能保證數據安全,但問題是連查票操做也變成只能一個一我的去查了,很明顯你們查票時應該是併發地去查詢而無需考慮數據準確與否,此時join與互斥鎖的區別就顯而易見了,join是將一個任務總體串行,而互斥鎖的好處則是能夠將一個任務中的某一段代碼串行,好比只讓task函數中的get任務串行。code

def task(name,):
    search(name)   # 併發執行
    
    lock.acquire()
    get(name)
    lock.release()

4、總結

加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,即串行地修改,沒有,速度是慢了,但犧牲了速度卻保證了數據安全。

雖然能夠用文件共享數據實現進程間通訊,但問題是:

一、效率低(共享數據基於文件,而文件是硬盤上的數據)

二、須要本身加鎖處理

所以咱們最好尋找一種解決方案可以兼顧:

一、效率高(多個進程共享一塊內存的數據)

二、幫咱們處理好鎖問題

這就是multiprocessing模塊爲咱們提供的基於消息的IPC通訊機制:隊列和管道。

隊列和管道都是將數據存放於內存中,而隊列又是基於[管道+鎖]實現的,可讓咱們從複雜的鎖問題中解脫出來,於是隊列纔是進程間通訊的最佳選擇。

咱們應該儘可能避免使用共享數據,儘量使用消息傳遞和隊列,避免處理複雜的同步和鎖問題,並且在進程數目增多時,每每能夠得到更好的可擴展性。

相關文章
相關標籤/搜索