Python之線程的同步互斥與死鎖

線程間通信方法

    1. 通信方法

線程間使用全局變量進行通信

    2. 共享資源爭奪

共享資源:多個進程或者線程都可以操作的資源稱爲共享資源。對共享資源的操作代碼段稱爲臨界區。

影響 : 對共享資源的無序操作可能會帶來數據的混亂,或者操作錯誤。此時往往需要同步互斥機制協調操作順序。

    3. 同步互斥機制

同步 : 同步是一種協作關係,爲完成操作,多進程或者線程間形成一種協調,按照必要的步驟有序執行操作。兩個或兩個以上的進程或線程在運行過程中協同步調,按預定的先後次序運行。比如 A 任務的運行依賴於 B 任務產生的數據。

互斥 : 互斥是一種制約關係,當一個進程或者線程佔有資源時會進行加鎖處理,此時其他進程線程就無法操作該資源,直到解鎖後才能操作。一個公共資源同一時刻只能被一個進程或線程使用,多個進程或線程不能同時使用公共資源

線程同步互斥方法

    線程Event同步

from threading import Event

e = Event() 創建線程event對象

e.wait([timeout]) 阻塞等待eset

e.set() 設置e,使wait結束阻塞

e.clear() 使e回到未被設置狀態

e.is_set() 查看當前e是否被設置

示例:

import time
import threading

event = threading.Event()


# 紅綠燈
def lighter():
    count = 0
    event.set()  # 剛進來的時候是綠燈
    while True:
        if 4 < count < 10:
            event.clear()  # 清除設置,阻塞等待
            print("[信號燈]:紅,不能通行", count)
        elif count >= 10:  # 添加設置,繼續執行
            event.set()
            count = 0
        else:
            event.set()  # 添加設置,繼續執行
            print("[信號燈]:綠燈,可以通行", count)
        time.sleep(1)
        count += 1


# 汽車
def car(name):
    while True:
        if event.is_set():
            print("{0}: 綠燈 , 走起...".format(name))
            time.sleep(1)
        else:
            print("{0}: 紅燈 , 停車...".format(name))
            event.wait()
            print("{0}: 綠燈亮了 , 繼續前進...".format(name))


light = threading.Thread(target=lighter, )
light.start()
car1 = threading.Thread(target=car, args=("小跑",))
car1.start()

    線程鎖 Lock

from threading import Lock

lock = Lock() 創建鎖對象

lock.acquire() 上鎖 如果lock已經上鎖再調用會阻塞

lock.release() 解鎖

with lock: 上鎖

with代碼塊結束自動解鎖

示例:

from threading import Thread, Lock
from time import sleep

a = b = 0
lock = Lock()


# 子線程輸出a b
def value():
    while True:
        lock.acquire()  # 上鎖
        if a != b:
            print("a = %d,b = %d" % (a, b))
        lock.release()  # 解鎖


t = Thread(target=value)
t.start()

# 主線程加鎖更改a b時候,子線程處理a b 時也要進行加鎖,重複加鎖就會阻塞等待主線程處理結束
# 同理主進程再次更改a b 時等 子進程結束纔可以
while True:
    with lock:  # 自動上/解鎖
        a += 1
        b += 1
t.join

死鎖及其處理

    1. 定義

        死鎖是指兩個或兩個以上的線程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖。

    2. 死鎖產生條件

        【互斥條件】:指線程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。

       【請求和保持條件】:指線程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求線程阻塞,但又對自己已獲得的其它資源保持不放。

        【不剝奪條件】:指線程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放,通常CPU內存資源是可以被系統強行調配剝奪的。

        【環路等待條件】:指在發生死鎖時,必然存在一個線程——資源的環形鏈,即進程集合{T0,T1T2···Tn}中的T0正在等待一個T1佔用的資源;T1正在等待T2佔用的資源,……Tn正在等待已被T0佔用的資源。

         簡單來說造成死鎖的原因可以概括成三句話:

【1】當前線程擁有其他線程需要的資源

【2】當前線程等待其他線程已擁有的資源

【3】都不放棄自己擁有的資源

 

T1擁有R1,T2擁有R2。T1請求使用R2,T2請求使用R1,但是T1,T2 都不願釋放R1,R2,互相一直等待下去,造成死鎖

    3. 如何避免死鎖

        死鎖是我們非常不願意看到的一種現象,我們要儘可能避免死鎖的情況發生。通過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或者幾個,來預防發生死鎖。預防死鎖是一種較易實現的方法。但是由於所施加的限制條件往往太嚴格,可能會導致系統資源利用率。

from threading import Lock, Thread


# 交易類
class Account:
    def __init__(self, _id, balance, lock):
        self.id = _id
        self.balance = balance
        self.lock = lock  # 各自賬戶鎖

    # 取錢
    def withdraw(self, amount):
        self.balance -= amount

    # 存錢
    def deposit(self, amount):
        self.balance += amount

    # 查看賬戶
    def get_balance(self):
        return self.balance


# 轉賬
def transfer(from_, to, amount):
    if from_.lock.acquire():  # 鎖住自己的賬戶
        from_.withdraw(amount)  # 自己賬戶減少
        if to.lock.acquire():  # 鎖住對方賬戶
            to.deposit(amount)  # 對方賬戶增加
            to.lock.release()  # 解鎖對方賬戶
        from_.lock.release()  # 自己賬戶解鎖
    print("轉賬完成")


Abby = Account("Abby", 5000, Lock())
Balen = Account("Balen", 3000, Lock())

t = Thread(target=transfer, args=(Abby, Balen, 1000))
t2 = Thread(target=transfer, args=(Balen, Abby, 500))
t.start()
t2.start()
t.join()
t2.join()

print("Abby:", Abby.get_balance())
print("Balen:", Balen.get_balance())

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章