Python多進程和多線程的同步互斥

同步互斥機制:

  • 解決了多個進程或者線程對共享資源的爭奪,因爲某些共享資源在某個時刻可能只允許一個進程對其進行訪問。

首先需要了解幾個概念:

臨界資源:

  • 臨界資源就是上面提到的,同時只允許一個進程對其進行訪問的資源,即:雖然是共享資源,但是不能多個進程同時對資源進行訪問和操作。多進程之間需要遵守某些約定來對臨界資源進行訪問和操作。

臨界區:

  • 臨界區就是我們寫的多進程代碼中,對臨界資源進行操作的那一部分代碼段(區域)。

之前說了多進程之間需要遵守某些約定來對臨界資源進行訪問和操作,就是同步互斥

同步:

  • 同步可以理解爲是一種合作關係,爲了完成某項任務(訪問臨界資源),多個進程之間通過合作協調,排好順序,依次操作。舉個例子,在醫院的一個診室外排了很多看病的人,基於重症排在前,輕症排在後的規則排好了順序,醫生就可以看成是一個臨界資源,醫生每次只能看一個病人(每次只有一個病人(進程)能進診室看醫生(臨界資源)),然後依次看完所有的病人。

互斥:

  • 互斥則是一種制約關係,進程訪問臨界資源時會上鎖,使得其他進程阻塞等待,直到上一個進程退出後解鎖,下一個進程才能夠訪問臨界資源。還是上面的例子:在醫院的一個診室外有很多看病的人,並沒有按照順序排着,醫生每次只能看一個病人,病人通過爭奪搶佔看誰先進入診室,一個病人進入診室後,就將診室的門鎖上(上鎖),阻止其他病人進入,直到這個病人看完後,打開門出了診室(解鎖),剩下的病人又開始爭奪搶佔看誰先進入診室。

Event事件

  • Event事件通常用於主進程控制其他進程的執行,Event事件主要提供了三個方法set、wait、clear。
  • 事件處理的機制:全局定義了一個標誌,如果標誌的值爲 False,那麼當程序執行到wait方法時就會阻塞。如果標誌值設爲True,那麼wait方法便不再阻塞了。
  • Event事件對象的set方法將標誌設爲true,clear方法將標誌設置爲False,wait方法則是在標誌值爲False時阻塞等待。

主要方法:

e = Event()

  • 功能:創建事件對象

e.wait([timeout])

  • 功能:設置事件阻塞
  • 參數(timeout):阻塞等待時間

e.set()

  • 功能:事件設置,當事件被設置後e.wait()不再阻塞

e.clear()

  • 功能:清除設置,當事件設置被清除後e.wait又會阻塞

e.is_set()

  • 功能:事件狀態判斷
  • 返回值:True(wait方法未阻塞),False(wait方法阻塞)

代碼實現:

from multiprocessing import Process,Event
from time import sleep

def test1():
    print("test1想操作文件")
    print("test11:", e.is_set())  # 判斷事件狀態
    e.wait()  # 設置事件阻塞
    print("test12:", e.is_set())  # 判斷事件狀態
    print("test2開始操作文件...")
    with open("test.txt") as f:
        print(f.read())
    e.clear()

def test2():
    print("test2也想操作文件")
    e.wait(2)  # 設置事件阻塞2秒
    if e.is_set():  # 判斷事件狀態
        with open("test.txt") as f:
            print(f.read())
    else:
        print("test2阻塞超時,不能讀取文件")

# 創建事件對象
e = Event()
p1 = Process(target = test1)
p2 = Process(target = test2)
p1.start()
p2.start()

print("主進程創建文件")
with open('test.txt','w') as f:
    sleep(3)
    f.write("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
e.set()  # e.wait()不再阻塞
print('釋放臨界區使其不再阻塞')

p1.join()
p2.join()

該實例中,一個主進程和兩個子進程對文件進行操作,在主進程創建文件並寫入內容之前,子進程一直等待,其中一個p2子進程設置了阻塞超時時間,所以p2子進程會先執行完退出,而子進程p1會一直阻塞等待主進程調用set方法。

運行結果:

gk@gk-vm:~/python/test$ python3 process_event.py 
主進程創建文件
test1想操作文件
test11: False
test2也想操作文件
test2阻塞超時,不能讀取文件
釋放臨界區使其不再阻塞
test12: True
test2開始操作文件...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Lock互斥鎖

  • 互斥鎖就是進程的互相排斥,誰先搶到資源,誰就上鎖改資源內容。
  • 同一時間只允許一個進程上一把鎖。

主要方法:
lock = Lock()

  • 功能:創建鎖對象

lock.acquire()

  • 功能:上鎖,如果鎖已經是上鎖狀態調用此函數會阻塞

lock.release()

  • 功能:解鎖

還有一種上鎖方法

  • with lock

代碼實現:

from multiprocessing import Process, Lock
import sys
from time import sleep

def test(n):
    lock.acquire()  # 上鎖
    for i in range(3):
        print("test{}".format(n))
    lock.release()  # 解鎖

# with lock上鎖
# def test(n):
# 	with lock:
# 	    for i in range(3):
# 	        print("test{}".format(n))

# 創建鎖對象
lock = Lock()

jobs = []
# 創建3個上鎖的進程
for n in range(3):
	p = Process(target = test, args = (n,))
	p.start()
	jobs.append(p)

for j in jobs:
	j.join()

該實例創建了三個上鎖的子進程。

運行結果:

gk@gk-vm:~/python/test$ python3 process_lock.py 
test0
test0
test0
test2
test2
test2
test1
test1
test1

觀察結果發現三個子進程執行順序是無序的,但是子進程之間並沒有交叉執行,說明了上鎖後同一時間只能執行一個進程,其他進程都會阻塞等待,而誰先上鎖是沒有順序的,進程需要自己搶佔資源,決定誰先執行。

另外:

  • Python多線程模塊Thread中也有Event事件和Lock鎖,用法和多進程的用法幾乎一樣。(注意區分多線程和多進程的原理)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章