欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中死鎖的形成示例及死鎖情況的防止

 更新時間:2016年06月14日 16:49:21   作者:mattkang  
由于Python中允許創(chuàng)建多個線程,那么互斥鎖或者線程同時獲取多個鎖的情況就有可能發(fā)生,這里我們就來看一下Python中死鎖的形成示例及死鎖情況的防止:

死鎖示例
搞多線程的經(jīng)常會遇到死鎖的問題,學(xué)習(xí)操作系統(tǒng)的時候會講到死鎖相關(guān)的東西,我們用Python直觀的演示一下。
死鎖的一個原因是互斥鎖。假設(shè)銀行系統(tǒng)中,用戶a試圖轉(zhuǎn)賬100塊給用戶b,與此同時用戶b試圖轉(zhuǎn)賬200塊給用戶a,則可能產(chǎn)生死鎖。
2個線程互相等待對方的鎖,互相占用著資源不釋放。

#coding=utf-8 
import time 
import threading 
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 transfer(_from, to, amount): 
  if _from.lock.acquire():#鎖住自己的賬戶 
    _from.withdraw(amount) 
    time.sleep(1)#讓交易時間變長,2個交易線程時間上重疊,有足夠時間來產(chǎn)生死鎖 
    print 'wait for lock...' 
    if to.lock.acquire():#鎖住對方的賬戶 
      to.deposit(amount) 
      to.lock.release() 
    _from.lock.release() 
  print 'finish...' 
 
a = Account('a',1000, threading.Lock()) 
b = Account('b',1000, threading.Lock()) 
threading.Thread(target = transfer, args = (a, b, 100)).start() 
threading.Thread(target = transfer, args = (b, a, 200)).start() 

防止死鎖的加鎖機(jī)制
問題:
你正在寫一個多線程程序,其中線程需要一次獲取多個鎖,此時如何避免死鎖問題。
解決方案:
在多線程程序中,死鎖問題很大一部分是由于線程同時獲取多個鎖造成的。舉個例子:一個線程獲取了第一個鎖,然后在獲取第二個鎖的 時候發(fā)生阻塞,那么這個線程就可能阻塞其他線程的執(zhí)行,從而導(dǎo)致整個程序假死。 解決死鎖問題的一種方案是為程序中的每一個鎖分配一個唯一的id,然后只允許按照升序規(guī)則來使用多個鎖,這個規(guī)則使用上下文管理器 是非常容易實(shí)現(xiàn)的,示例如下:

import threading
from contextlib import contextmanager

# Thread-local state to stored information on locks already acquired
_local = threading.local()

@contextmanager
def acquire(*locks):
  # Sort locks by object identifier
  locks = sorted(locks, key=lambda x: id(x))

  # Make sure lock order of previously acquired locks is not violated
  acquired = getattr(_local,'acquired',[])
  if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
    raise RuntimeError('Lock Order Violation')

  # Acquire all of the locks
  acquired.extend(locks)
  _local.acquired = acquired

  try:
    for lock in locks:
      lock.acquire()
    yield
  finally:
    # Release locks in reverse order of acquisition
    for lock in reversed(locks):
      lock.release()
    del acquired[-len(locks):]

如何使用這個上下文管理器呢?你可以按照正常途徑創(chuàng)建一個鎖對象,但不論是單個鎖還是多個鎖中都使用 acquire() 函數(shù)來申請鎖, 示例如下:

import threading
x_lock = threading.Lock()
y_lock = threading.Lock()

def thread_1():
  while True:
    with acquire(x_lock, y_lock):
      print('Thread-1')

def thread_2():
  while True:
    with acquire(y_lock, x_lock):
      print('Thread-2')

t1 = threading.Thread(target=thread_1)
t1.daemon = True
t1.start()

t2 = threading.Thread(target=thread_2)
t2.daemon = True
t2.start()

如果你執(zhí)行這段代碼,你會發(fā)現(xiàn)它即使在不同的函數(shù)中以不同的順序獲取鎖也沒有發(fā)生死鎖。 其關(guān)鍵在于,在第一段代碼中,我們對這些鎖進(jìn)行了排序。通過排序,使得不管用戶以什么樣的順序來請求鎖,這些鎖都會按照固定的順序被獲取。 如果有多個 acquire() 操作被嵌套調(diào)用,可以通過線程本地存儲(TLS)來檢測潛在的死鎖問題。 假設(shè)你的代碼是這樣寫的:

import threading
x_lock = threading.Lock()
y_lock = threading.Lock()

def thread_1():

  while True:
    with acquire(x_lock):
      with acquire(y_lock):
        print('Thread-1')

def thread_2():
  while True:
    with acquire(y_lock):
      with acquire(x_lock):
        print('Thread-2')

t1 = threading.Thread(target=thread_1)
t1.daemon = True
t1.start()

t2 = threading.Thread(target=thread_2)
t2.daemon = True
t2.start()

如果你運(yùn)行這個版本的代碼,必定會有一個線程發(fā)生崩潰,異常信息可能像這樣:

Exception in thread Thread-1:
Traceback (most recent call last):
 File "/usr/local/lib/python3.3/threading.py", line 639, in _bootstrap_inner
  self.run()
 File "/usr/local/lib/python3.3/threading.py", line 596, in run
  self._target(*self._args, **self._kwargs)
 File "deadlock.py", line 49, in thread_1
  with acquire(y_lock):
 File "/usr/local/lib/python3.3/contextlib.py", line 48, in __enter__
  return next(self.gen)
 File "deadlock.py", line 15, in acquire
  raise RuntimeError("Lock Order Violation")
RuntimeError: Lock Order Violation
>>>

發(fā)生崩潰的原因在于,每個線程都記錄著自己已經(jīng)獲取到的鎖。 acquire() 函數(shù)會檢查之前已經(jīng)獲取的鎖列表, 由于鎖是按照升序排列獲取的,所以函數(shù)會認(rèn)為之前已獲取的鎖的id必定小于新申請到的鎖,這時就會觸發(fā)異常。

討論
死鎖是每一個多線程程序都會面臨的一個問題(就像它是每一本操作系統(tǒng)課本的共同話題一樣)。根據(jù)經(jīng)驗來講,盡可能保證每一個 線程只能同時保持一個鎖,這樣程序就不會被死鎖問題所困擾。一旦有線程同時申請多個鎖,一切就不可預(yù)料了。

死鎖的檢測與恢復(fù)是一個幾乎沒有優(yōu)雅的解決方案的擴(kuò)展話題。一個比較常用的死鎖檢測與恢復(fù)的方案是引入看門狗計數(shù)器。當(dāng)線程正常 運(yùn)行的時候會每隔一段時間重置計數(shù)器,在沒有發(fā)生死鎖的情況下,一切都正常進(jìn)行。一旦發(fā)生死鎖,由于無法重置計數(shù)器導(dǎo)致定時器 超時,這時程序會通過重啟自身恢復(fù)到正常狀態(tài)。

避免死鎖是另外一種解決死鎖問題的方式,在進(jìn)程獲取鎖的時候會嚴(yán)格按照對象id升序排列獲取,經(jīng)過數(shù)學(xué)證明,這樣保證程序不會進(jìn)入 死鎖狀態(tài)。證明就留給讀者作為練習(xí)了。避免死鎖的主要思想是,單純地按照對象id遞增的順序加鎖不會產(chǎn)生循環(huán)依賴,而循環(huán)依賴是 死鎖的一個必要條件,從而避免程序進(jìn)入死鎖狀態(tài)。

下面以一個關(guān)于線程死鎖的經(jīng)典問題:“哲學(xué)家就餐問題”,作為本節(jié)最后一個例子。題目是這樣的:五位哲學(xué)家圍坐在一張桌子前,每個人 面前有一碗飯和一只筷子。在這里每個哲學(xué)家可以看做是一個獨(dú)立的線程,而每只筷子可以看做是一個鎖。每個哲學(xué)家可以處在靜坐、 思考、吃飯三種狀態(tài)中的一個。需要注意的是,每個哲學(xué)家吃飯是需要兩只筷子的,這樣問題就來了:如果每個哲學(xué)家都拿起自己左邊的筷子, 那么他們五個都只能拿著一只筷子坐在那兒,直到餓死。此時他們就進(jìn)入了死鎖狀態(tài)。 下面是一個簡單的使用死鎖避免機(jī)制解決“哲學(xué)家就餐問題”的實(shí)現(xiàn):

import threading

# The philosopher thread
def philosopher(left, right):
  while True:
    with acquire(left,right):
       print(threading.currentThread(), 'eating')

# The chopsticks (represented by locks)
NSTICKS = 5
chopsticks = [threading.Lock() for n in range(NSTICKS)]

# Create all of the philosophers
for n in range(NSTICKS):
  t = threading.Thread(target=philosopher,
             args=(chopsticks[n],chopsticks[(n+1) % NSTICKS]))
  t.start()

最后,要特別注意到,為了避免死鎖,所有的加鎖操作必須使用 acquire() 函數(shù)。如果代碼中的某部分繞過acquire 函數(shù)直接申請鎖,那么整個死鎖避免機(jī)制就不起作用了。

相關(guān)文章

  • 詳解python中@classmethod和@staticmethod方法

    詳解python中@classmethod和@staticmethod方法

    在python類當(dāng)中,經(jīng)常會遇到@classmethod和@staticmethod這兩個裝飾器,那么到底它們的區(qū)別和作用是啥子呢?本文結(jié)合場景分析給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧
    2022-10-10
  • python實(shí)現(xiàn)字符串加密 生成唯一固定長度字符串

    python實(shí)現(xiàn)字符串加密 生成唯一固定長度字符串

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)字符串加密,生成唯一固定長度字符串,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • python怎么創(chuàng)建新文件代碼舉例

    python怎么創(chuàng)建新文件代碼舉例

    Python創(chuàng)建新文件有多種方法,包括使用open函數(shù)、with語句和Pathlib模塊,這篇文章主要給大家介紹了關(guān)于python怎么創(chuàng)建新文件的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • python+playwright微軟自動化工具的使用

    python+playwright微軟自動化工具的使用

    這篇文章主要介紹了python+playwright微軟自動化工具的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Python如何根據(jù)字幕文件自動給視頻添加字幕效果

    Python如何根據(jù)字幕文件自動給視頻添加字幕效果

    視頻中字幕的重要性不用多說了,下面這篇文章主要給大家介紹了關(guān)于Python如何根據(jù)字幕文件自動給視頻添加字幕效果的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • 利用Python的PyPDF2庫提取pdf中的圖片

    利用Python的PyPDF2庫提取pdf中的圖片

    本篇給大家分享一下通過Python的PyPDF2庫提取pdf中的圖片方法,文中有詳細(xì)的代碼示例和流程步驟,感興趣的同學(xué)可以閱讀一下
    2023-05-05
  • 關(guān)于pygame自定義窗口創(chuàng)建及相關(guān)操作指南

    關(guān)于pygame自定義窗口創(chuàng)建及相關(guān)操作指南

    對于開發(fā)一個游戲來說,窗口的顯示肯定是前提中的前提,對于pygame來說,只需要一小段代碼就可以初始化窗口,下面這篇文章主要給大家介紹了關(guān)于pygame自定義窗口創(chuàng)建及相關(guān)操作的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • python中對開區(qū)間和閉區(qū)間的理解

    python中對開區(qū)間和閉區(qū)間的理解

    這篇文章主要介紹了python中對開區(qū)間和閉區(qū)間的理解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Python戀愛小助手之必拿下

    Python戀愛小助手之必拿下

    小編有個好兄弟最近在追妹子,跟妹子打得火熱!就差臨門一腳了,這一jio我?guī)兔ρa(bǔ)上去了!他問有沒有什么酷炫的表白方式,想要一次成功,表白的方式有許多種今天小編來給大家?guī)韮蓚€不得不同意的表白代碼
    2021-10-10
  • Python實(shí)現(xiàn)針對含中文字符串的截取功能示例

    Python實(shí)現(xiàn)針對含中文字符串的截取功能示例

    這篇文章主要介紹了Python實(shí)現(xiàn)針對含中文字符串的截取功能,結(jié)合具體實(shí)例形式分析了Python針對utf-8及gb18030編碼的中文字符串截取操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09

最新評論