進程同步控制--鎖、信號量、事件

********進程同步控制********


****鎖--multiprocessing.Lock****

經過學習,我麼實現了程序的異步,讓多個任務能夠同時在幾個進程中處理,他們之間的運行沒有順序,一旦
開啓不受咱們控制。儘管併發編程讓咱們能更加充分的利用IO資源,可是也給咱們帶來了新的問題

當多個進程使用同一份數據資源的時候,就會引起數據安全或順序混亂問題。


**多進程搶佔資源

import os
import time
import random
from multiprocessing import Process


def work(n):
    print('%s: %s is running' % (n, os.getpid()))
    time.sleep(random.random())
    print('%s:%s is done' % (n, os.getpid()))


if __name__ == '__main__':
    for i in range(3):
        p = Process(target=work, args=(i,))
        p.start()

**使用鎖維護執行順序
由併發變成了串行,犧牲了運行效率,但避免了競爭

import time
import random
import os
from multiprocessing import Process,Lock


def work(n, l):
    l.acquire()
    print('%s: %s is running' % (n, os.getpid()))
    time.sleep(random.random())
    print('%s:%s is done' % (n, os.getpid()))
    l.release()

if __name__ == '__main__':
    l = Lock()
    for i in range(3):
        p = Process(target=work, args=(i,l))
        p.start()

上面這種狀況雖然使用加鎖的形式實現了順序的執行,可是程序又從新變成串行了,這樣確實會浪費了時間,卻保證了數據的安全。

接下來,咱們以模擬搶票爲例,來看看數據安全的重要性。

from multiprocessing import Lock, Process
import time


def show(i):  # 產看餘票信息
    with open('餘票') as f:
        temp = f.read()
    print('第%s我的查到還有餘票%s' % (i, temp))


def buy(i, l):  # 買票
    l.acquire()
    with open('餘票') as f:
        temp = int(f.read())
    time.sleep(0.1)
    if temp > 0:
        temp -= 1
        print('\033[31m 第%s我的買到了票\033[0m' % i)
    else:
        print('\033[32m第%s我的沒有買到票\033[0m' % i)
    time.sleep(0.1)
    with open('餘票', 'w') as f:
        f.write(str(temp))
    l.release()


if __name__ == '__main__':

    l = Lock()

    for i in range(10):  # 多用戶查看票數
        p = Process(target=show, args=(i + 1,))
        p.start()

    for i in range(10):  # 模擬併發多個用戶搶票
        p = Process(target=buy, args=(i + 1, l))
        p.start()


#加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,
#即串行的修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。
雖然能夠用文件共享數據實現進程間通訊,但問題是:
1.效率低(共享數據基於文件,而文件是硬盤上的數據)
2.須要本身加鎖處理

#所以咱們最好找尋一種解決方案可以兼顧:
1、效率高(多個進程共享一塊內存的數據)
2、幫咱們處理好鎖問題。這就是mutiprocessing模塊爲咱們提供的基於消息的IPC通訊機制:隊列和管道。
隊列和管道都是將數據存放於內存中
隊列又是基於(管道+鎖)實現的,可讓咱們從複雜的鎖問題中解脫出來,
咱們應該儘可能避免使用共享數據,儘量使用消息傳遞和隊列,避免處理複雜的同步和鎖問題,
並且在進程數目增多時,每每能夠得到更好的可獲展性。



****信號量****

介紹:
互斥鎖同時只容許一個線程更改數據,而信號量Semaphore是同時容許必定數量的線程更改數據 。
假設商場裏有4個迷你唱吧,因此同時能夠進去4我的,若是來了第五我的就要在外面等待,等到有人出來才能再進去玩。
實現:
信號量同步基於內部計數器,每調用一次acquire(),計數器減1;每調用一次release(),計數器加1.當計數器爲0時,
acquire()調用被阻塞。這是迪科斯徹(Dijkstra)信號量概念P()和V()的Python實現。信號量同步機制適用於訪問
像服務器這樣的有限資源。

信號量與進程池的概念很像,可是要區分開,信號量涉及到加鎖的概念

from multiprocessing import Process, Semaphore
import time, random


def work(sem, user):
    sem.acquire()
    print('%s 佔到一個房間' % user)
    time.sleep(random.random())  # 模擬在房間中待的時間
    sem.release()


if __name__ == '__main__':
    sem = Semaphore(4)
    p_ls = []
    for i in range(10):
        p = Process(target=work, args=(sem, 'user %s' % i,))
        p.start()
        p_ls.append(p)

    [i.join() for i in p_ls]
    print('>>>>>>>>>>>>>>>>>>>')


****事件****


python線程的事件用於主線程控制其餘線程的執行,事件主要提供了三個方法 set、wait、clear。

事件處理的機制:is_set() 方法的值,默認爲False
那麼當程序執行 event.wait 方法時就會阻塞,
若是is_set()值爲True,那麼event.wait 方法執行時便再也不阻塞。

clear()方法:將is_set()的值設置爲False
set()方法:將is_set()的值設置爲True



紅綠燈實例:

from multiprocessing import Process, Event
import time
import random


# 亮燈和車行駛是異步的
# 亮燈的進程先開啓
# is_set()剛開始默認爲False
# 先開始l綠燈亮,這個時候car的進程開啓,is_set()爲True,能夠過車

def light(e):
    while 1:
        if e.is_set():
            e.clear()
            print('\033[31m紅燈亮了\033[0m')
        else:
            e.set()
            print('\033[32m綠燈亮了\033[0m')
        time.sleep(2)


def cars(i, e):
    if not e.is_set():
        print('第%s輛車在等待' % i)
        e.wait()
    print('第%s輛車過去了' % i)


if __name__ == '__main__':

    e = Event()
    tr = Process(target=light, args=(e,))  # 建立一個進程控制紅綠燈
    tr.start()

    for i in range(50):  # 建立50個進程表明50輛車
        car = Process(target=cars, args=(i + 1, e))
        car.start()
        time.sleep(random.random())
相關文章
相關標籤/搜索