python 之 併發程式設計(守護程式、互斥鎖、IPC通訊機制)

發表於2019-07-19

9.5 守護程式

主程式建立守護程式

  其一:守護程式會在主程式程式碼執行結束後就立即終止

  其二:守護程式內無法再開啟子程式,否則丟擲異常:AssertionError: daemonic processes are not allowed to have children

注意:程式之間是互相獨立的,主程式程式碼執行結束,守護程式隨即終止

  • p.daemon:預設值為False,如果設為True,代表p為後臺執行的守護程式,當p的父程式終止時,p也隨之終止,並且設定為True後,p不能建立自己的新程式,必須在p.start()之前設定

from multiprocessing import Process
​
def task(name):
    print('%s is running' % name)
    time.sleep(3)
​
if __name__ == '__main__':
    obj = Process(target=task, args=('egon',))
    obj.daemon=True         #設定obj為守護程式,並且父程式程式碼執行結束,obj即終止執行
    obj.start()             # 傳送訊號給作業系統
    print('')

9.6 互斥鎖

互斥鎖用來將併發程式設計序列,犧牲了效率而保證了資料安全

強調:必須是lock.acquire()一次,然後 lock.release()釋放一次,才能繼續lock.acquire(),不能連續的lock.acquire()

互斥鎖和 join的區別:

二者的原理都是一樣,都是將併發變成序列,從而保證有序

區別一:join是按照人為指定的順序執行,而互斥鎖是程式平等地競爭,誰先搶到誰執行,一個人拿到鎖,其餘人都等待

from multiprocessing import Process,Lock
import random
​
mutex=Lock()
def task1(lock):
    lock.acquire() 
    print('task1:名字是egon')
    print('task1:性別是male')
    lock.release()
    
def task2(lock):
    lock.acquire()
    print('task2:名字是alex')
    print('task2:性別是male')
    lock.release()
    
def task3(lock):
    lock.acquire()
    print('task3:名字是lxx')
    print('task3:性別是female')
    lock.release()
    
if __name__ == '__main__':
    p1=Process(target=task1,args=(mutex,))
    p2=Process(target=task2,args=(mutex,))
    p3=Process(target=task3,args=(mutex,))
    p1.start()# p1.start()
    p2.start()# p1.join()
    p3.start()# p2.start()
             # p2.join()
             # p3.start()
             # p3.join()

9.61 模擬搶票

互斥鎖和 join的區別二:

互斥鎖可以讓一部分程式碼(修改共享資料的程式碼)序列,而join只能將程式碼整體序列

import json
import time
import random
import os
from multiprocessing import Process,Lock
​
mutex=Lock()
def search():
    time.sleep(random.randint(1,3))
    with open('db.json','r',encoding='utf-8') as f:
        dic=json.load(f)
        print('%s 剩餘票數:%s' %(os.getpid(),dic['count']))
​
def get():
    with open('db.json','r',encoding='utf-8') as f:
        dic=json.load(f)
    if dic['count'] > 0:
        dic['count']-=1
        with open('db.json','w',encoding='utf-8') as f:
            json.dump(dic,f)
        print('%s 購票成功' %os.getpid())
​
def task(lock):
    search()
    lock.acquire()
    get()
    lock.release()
​
if __name__ == '__main__':
    for i in range(10):
        p=Process(target=task,args=(mutex,))
        p.start()
        # p.join()

9.7 IPC通訊機制

程式彼此之間互相隔離,要實現程式間通訊(IPC),multiprocessing模組支援兩種形式:佇列和管道,這兩種方式都是使用訊息傳遞的

程式之間通訊必須找到一種介質,該介質必須滿足: 1、是所有程式共享的 2、必須是記憶體空間 附加:幫我們自動處理好鎖的問題

from multiprocessing import Process,Manager,Lock
import time
​
mutex=Lock()
def task(dic,lock):
    lock.acquire()
    temp=dic['num']
    time.sleep(0.1)
    dic['num']=temp-1
    lock.release()
​
if __name__ == '__main__':
    m=Manager()
    dic=m.dict({'num':10})
    l=[]
    for i in range(10):
        p=Process(target=task,args=(dic,mutex))
        l.append(p)
        p.start()
    for p in l:
        p.join()
    print(dic)      #{'num': 0}

9.71建立佇列的類Queue

底層就是以管道和鎖定的方式實現:

佇列 (管道+鎖) :1、共享的空間 2、是記憶體空間 3、自動幫我們處理好鎖定問題

  • Queue([maxsize]):建立共享的程式佇列,Queue是多程式安全的佇列,可以使用Queue實現多程式之間的資料傳遞。

  • maxsize是佇列中允許最大項數,省略則無大小限制。

from multiprocessing import Queue
q=Queue(3)      #maxsize=3
q.put('first')
q.put({'second':None})
q.put('')
​
# q.put(4) #阻塞
print(q.get())  #first
print(q.get())  #{'second': None}
print(q.get())  #

強調: 1、佇列用來存成程式之間溝通的訊息,資料量不應該過大 2、maxsize的值超過的記憶體限制就變得毫無意義

瞭解:block=True(預設值)、timeout

q=Queue(1)
q.put('first',block=False)      #q.put方法用以插入資料到佇列中
q.put('fourth',block=False/True)#queue.Full/一直等
​
q.put('first',block=True)
q.put('fourth',block=True,timeout=3)#等3秒後報錯queue.Full
​
q.get(block=False)#q.get方法可以從佇列讀取並且刪除一個元素
q.get(block=False)#queue.Empty
​
q.get(block=True)
q.get(block=True,timeout=2)#等2秒後報錯queue.Empty

相關文章