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

Python 防止死鎖的方法

 更新時(shí)間:2020年07月29日 11:39:15   作者:D  
這篇文章主要介紹了Python 防止死鎖的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

問(wèn)題

你正在寫(xiě)一個(gè)多線程程序,其中線程需要一次獲取多個(gè)鎖,此時(shí)如何避免死鎖問(wèn)題。

解決方案

在多線程程序中,死鎖問(wèn)題很大一部分是由于線程同時(shí)獲取多個(gè)鎖造成的。舉個(gè)例子:一個(gè)線程獲取了第一個(gè)鎖,然后在獲取第二個(gè)鎖的 時(shí)候發(fā)生阻塞,那么這個(gè)線程就可能阻塞其他線程的執(zhí)行,從而導(dǎo)致整個(gè)程序假死。 解決死鎖問(wèn)題的一種方案是為程序中的每一個(gè)鎖分配一個(gè)唯一的id,然后只允許按照升序規(guī)則來(lái)使用多個(gè)鎖,這個(gè)規(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):]

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

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í)行這段代碼,你會(huì)發(fā)現(xiàn)它即使在不同的函數(shù)中以不同的順序獲取鎖也沒(méi)有發(fā)生死鎖。 其關(guān)鍵在于,在第一段代碼中,我們對(duì)這些鎖進(jìn)行了排序。通過(guò)排序,使得不管用戶(hù)以什么樣的順序來(lái)請(qǐng)求鎖,這些鎖都會(huì)按照固定的順序被獲取。 如果有多個(gè) acquire() 操作被嵌套調(diào)用,可以通過(guò)線程本地存儲(chǔ)(TLS)來(lái)檢測(cè)潛在的死鎖問(wèn)題。 假設(shè)你的代碼是這樣寫(xiě)的:

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)行這個(gè)版本的代碼,必定會(huì)有一個(gè)線程發(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ā)生崩潰的原因在于,每個(gè)線程都記錄著自己已經(jīng)獲取到的鎖。 acquire() 函數(shù)會(huì)檢查之前已經(jīng)獲取的鎖列表, 由于鎖是按照升序排列獲取的,所以函數(shù)會(huì)認(rèn)為之前已獲取的鎖的id必定小于新申請(qǐng)到的鎖,這時(shí)就會(huì)觸發(fā)異常。

討論

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

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

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

下面以一個(gè)關(guān)于線程死鎖的經(jīng)典問(wèn)題:“哲學(xué)家就餐問(wèn)題”,作為本節(jié)最后一個(gè)例子。題目是這樣的:五位哲學(xué)家圍坐在一張桌子前,每個(gè)人 面前有一碗飯和一只筷子。在這里每個(gè)哲學(xué)家可以看做是一個(gè)獨(dú)立的線程,而每只筷子可以看做是一個(gè)鎖。每個(gè)哲學(xué)家可以處在靜坐、 思考、吃飯三種狀態(tài)中的一個(gè)。需要注意的是,每個(gè)哲學(xué)家吃飯是需要兩只筷子的,這樣問(wèn)題就來(lái)了:如果每個(gè)哲學(xué)家都拿起自己左邊的筷子, 那么他們五個(gè)都只能拿著一只筷子坐在那兒,直到餓死。此時(shí)他們就進(jìn)入了死鎖狀態(tài)。 下面是一個(gè)簡(jiǎn)單的使用死鎖避免機(jī)制解決“哲學(xué)家就餐問(wèn)題”的實(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ù)。如果代碼中的某部分繞過(guò)acquire 函數(shù)直接申請(qǐng)鎖,那么整個(gè)死鎖避免機(jī)制就不起作用了。

以上就是Python 防止死鎖的方法的詳細(xì)內(nèi)容,更多關(guān)于Python 防止死鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • numpy中np.sort返回索引的具體使用

    numpy中np.sort返回索引的具體使用

    本文主要介紹了使用numpy中np.sort函數(shù)返回索引的詳細(xì)解釋和使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Python利用subplots_adjust方法解決圖表與畫(huà)布的間距問(wèn)題

    Python利用subplots_adjust方法解決圖表與畫(huà)布的間距問(wèn)題

    這篇文章主要介紹了如何在使用python?的?matplotlib庫(kù)繪圖時(shí),?使用subplots_adjust()方法來(lái)調(diào)整圖表與畫(huà)布之間的間距,以及圖表與圖表之間的間距,感興趣的可以了解一下
    2022-04-04
  • Python解惑之True和False詳解

    Python解惑之True和False詳解

    這篇文章主要給大家介紹了關(guān)于Python中常用的數(shù)據(jù)類(lèi)型bool(布爾)類(lèi)型的兩個(gè)值:True和False的相關(guān)資料,通過(guò)示例代碼給大家進(jìn)行了解惑,讓對(duì)這兩個(gè)值有所疑惑的朋友們能有起到一定的幫助,需要的朋友下面來(lái)一起看看吧。
    2017-04-04
  • Python3 列表,數(shù)組,矩陣的相互轉(zhuǎn)換的方法示例

    Python3 列表,數(shù)組,矩陣的相互轉(zhuǎn)換的方法示例

    這篇文章主要介紹了Python3 列表,數(shù)組,矩陣的相互轉(zhuǎn)換的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 關(guān)于Flask 視圖介紹

    關(guān)于Flask 視圖介紹

    這篇文章主要分享的是關(guān)于Flask 視圖介紹, Flask 中路由是請(qǐng)求的 url 與處理函數(shù)之間的映射,使用app.route裝飾器將處理函數(shù)和 url 綁定,路由綁定的處理函數(shù)就被成為視圖函數(shù)。下面來(lái)看文章的詳細(xì)內(nèi)容,需要的朋友也可以參考一下
    2021-11-11
  • EM算法的python實(shí)現(xiàn)的方法步驟

    EM算法的python實(shí)現(xiàn)的方法步驟

    本篇文章主要介紹了EM算法的python實(shí)現(xiàn)的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • 通俗講解Python中的五種下劃線含義

    通俗講解Python中的五種下劃線含義

    在Python中,下劃線(_)是特殊的,如果您是python程序員,對(duì)于for _ in range(10),以及__init__(self)的語(yǔ)法可能比較熟悉,這篇文章主要給大家介紹了關(guān)于Python中五種下劃線的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • python的數(shù)學(xué)算法函數(shù)及公式用法

    python的數(shù)學(xué)算法函數(shù)及公式用法

    在本篇內(nèi)容里小編給大家分享了一篇關(guān)于python的數(shù)學(xué)算法函數(shù)及公式用法,有興趣的朋友們可以參考下。
    2020-11-11
  • 通過(guò)Python實(shí)現(xiàn)控制手機(jī)詳解

    通過(guò)Python實(shí)現(xiàn)控制手機(jī)詳解

    如今python是非常多人學(xué)習(xí)的,而手機(jī)也幾乎人手一部。對(duì)于很多Python學(xué)習(xí)者,想用python來(lái)完成android手機(jī)中各種炫酷的的控制,adb是必不可缺少的工具之一
    2021-10-10
  • python3.5基于TCP實(shí)現(xiàn)文件傳輸

    python3.5基于TCP實(shí)現(xiàn)文件傳輸

    這篇文章主要為大家詳細(xì)介紹了python3.5基于TCP實(shí)現(xiàn)文件傳輸?shù)拇a,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07

最新評(píng)論