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

Python?模擬死鎖的常見實例詳解

 更新時間:2022年08月30日 14:27:54   作者:宇宙之一粟  
這篇文章主要為大家介紹了Python?模擬死鎖的常見實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

常見的例子是在銀行賬戶上:假如要在兩個銀行賬戶之間執(zhí)行交易,你必須確保兩個賬戶都被鎖定,不受其他交易的影響,以達到正確的資金轉(zhuǎn)移量。在這里,這個類比并不完全成立--哲學(xué)家對應(yīng)的是鎖定賬戶的交易(分叉)--但同樣的技術(shù)困難也會出現(xiàn)。

其他的例子包括電商秒殺系統(tǒng),多個用戶搶一個商品,不允許一個數(shù)據(jù)庫被多個客戶同時修改。

死鎖也是由一個并發(fā)程序需要同時具備的條件來定義的,這樣才會發(fā)生死鎖。這些條件是由計算機科學(xué)家Edward G. Coffman, Jr .首先提出的,因此被稱為 Coffman 條件。這些條件如下:

  • 至少有一個資源必須處于不可共享的狀態(tài)。這意味著該資源被一個單獨的進程(或線程)持有,不能被其他人訪問; 在任何時間內(nèi),該資源只能被單個的進程(或線程)訪問和持有。這個條件也被稱為相互排斥。
  • 有一個進程(或線程)同時訪問一個資源并等待其他進程(或線程)持有的另一個資源。換句話說,這個進程(或線程)需要訪問兩個資源來執(zhí)行其指令,其中一個它已經(jīng)持有,另一個它正在等待其他進程(或線程)。這種情況被稱為保持和等待。
  • 只有在有特定指令讓進程(或線程)釋放資源的情況下,才能由持有這些資源的進程(或線程)來釋放。這就是說,除非進程(或線程)自愿主動地釋放資源,否則該資源仍處于不可共享的狀態(tài)。這就是無搶占條件。
  • 最后一個條件叫做循環(huán)等待。顧名思義,這個條件規(guī)定了一組進程(或線程)的存在,因此這組進程中的第一個進程(或線程)正在等待第二個進程(或線程)釋放資源,而第二個進程(或線程)又需要等待第三個進程(或線程);最后,這組進程中的最后一個進程(或線程)正在等待第一個進程。

造成線程死鎖的常見例子包括:

  • 一個在自己身上等待的線程(例如,試圖兩次獲得同一個互斥鎖)
  • 互相等待的線程(例如,A 等待 B,B 等待 A)
  • 未能釋放資源的線程(例如,互斥鎖、信號量、屏障、條件、事件等)
  • 線程以不同的順序獲取互斥鎖(例如,未能執(zhí)行鎖排序)

模擬死鎖1:線程等待本身

導(dǎo)致死鎖的一個常見原因是線程在自己身上等待。

我們并不打算讓這種死鎖發(fā)生,例如,我們不會故意寫代碼,導(dǎo)致線程自己等待。相反,由于一系列的函數(shù)調(diào)用和變量的傳遞,這種情況會意外地發(fā)生。

一個線程可能會因為很多原因而在自己身上等待,比如:

  • 等待獲得它已經(jīng)獲得的互斥鎖
  • 等待自己被通知一個條件
  • 等待一個事件被自己設(shè)置
  • 等待一個信號被自己釋放

開發(fā)一個 task() 函數(shù),直接嘗試兩次獲取同一個 mutex 鎖。也就是說,該任務(wù)將獲取鎖,然后再次嘗試獲取鎖。

# task to be executed in a new thread
def task(lock):
    print('Thread acquiring lock...')
    with lock:
        print('Thread acquiring lock again...')
        with lock:
            # will never get here
            pass

這將導(dǎo)致死鎖,因為線程已經(jīng)持有該鎖,并將永遠等待自己釋放該鎖,以便它能再次獲得該鎖, task() 試圖兩次獲取同一個鎖并觸發(fā)死鎖。

在主線程中,可以創(chuàng)建鎖:

# create the mutex lock
lock = Lock()

然后我們將創(chuàng)建并配置一個新的線程,在一個新的線程中執(zhí)行我們的 task() 函數(shù),然后啟動這個線程并等待它終止,而它永遠不會終止。

# create and configure the new thread
thread = Thread(target=task, args=(lock,))
# start the new thread
thread.start()
# wait for threads to exit...
thread.join()

完整代碼如下:

from threading import Thread
from threading import Lock
# task to be executed in a new thread
def task(lock):
    print('Thread acquiring lock...')
    with lock:
        print('Thread acquiring lock again...')
        with lock:
            # will never get here
            pass
# create the mutex lock
lock = Lock()
# create and configure the new thread
thread = Thread(target=task, args=(lock,))
# start the new thread
thread.start()
# wait for threads to exit...
thread.join()

運行結(jié)果如下:

首先創(chuàng)建鎖,然后新的線程被混淆并啟動,主線程阻塞,直到新線程終止,但它從未這樣做。

新線程運行并首先獲得了鎖。然后它試圖再次獲得相同的互斥鎖并阻塞。

它將永遠阻塞,等待鎖被釋放。該鎖不能被釋放,因為該線程已經(jīng)持有該鎖。因此,該線程已經(jīng)陷入死鎖。

該程序必須被強制終止,例如,通過 Control-C 殺死終端。

模擬死鎖2:線程互相等待

一個常見的例子就是兩個或多個線程互相等待。例如:線程 A 等待線程 B,線程 B 等待線程 A。

如果有三個線程,可能會出現(xiàn)線程循環(huán)等待,例如:

  • 線程 A:等待線程 B
  • 線程 B:等待線程 C
  • 線程 C:等待線程 A

如果你設(shè)置了線程來等待其他線程的結(jié)果,這種死鎖是很常見的,比如在一個流水線或工作流中,子任務(wù)的一些依賴關(guān)系是不符合順序的。

from threading import current_thread
from threading import Thread
# task to be executed in a new thread
def task(other):
    # message
    print(f'[{current_thread().name}] waiting on [{other.name}]...\n')
    other.join()
# get the current thread
main_thread = current_thread()
# create the second thread
new_thread = Thread(target=task, args=(main_thread,))
# start the new thread
new_thread.start()
# run the first thread
task(new_thread)

首先得到主線程的實例 main_thread,然后創(chuàng)建一個新的線程 new_thread,并調(diào)用傳遞給主線程的 task() 函數(shù)。新線程返回一條信息并等待主線程停止,主線程用新線程的實例調(diào)用 task()函數(shù),并等待新線程的終止。每個線程都在等待另一個線程終止,然后自己才能終止,這導(dǎo)致了一個死鎖。

運行結(jié)果:

[Thread-1] waiting on [MainThread]...
[MainThread] waiting on [Thread-1]...

模擬死鎖3:以錯誤的順序獲取鎖

導(dǎo)致死鎖的一個常見原因是,兩個線程同時以不同的順序獲得鎖。例如,我們可能有一個受鎖保護的關(guān)鍵部分,在這個關(guān)鍵部分中,我們可能有代碼或函數(shù)調(diào)用受第二個鎖保護。

可能會遇到這樣的情況:一個線程獲得了鎖 1 ,然后試圖獲得鎖 2,然后有第二個線程調(diào)用獲得鎖 2 的功能,然后試圖獲得鎖 1。如果這種情況同時發(fā)生,線程 1 持有鎖 1,線程 2 持有鎖 2,那么就會有一個死鎖。

  • 線程1: 持有鎖 1, 等待鎖 2
  • 線程2 : 持有鎖 2, 等待鎖 1
from time import sleep
from threading import Thread
from threading import Lock
# task to be executed in a new thread
def task(number, lock1, lock2):
    # acquire the first lock
    print(f'Thread {number} acquiring lock 1...')
    with lock1:
        # wait a moment
        sleep(1)
        # acquire the next lock
        print(f'Thread {number} acquiring lock 2...')
        with lock2:
            # never gets here..
            pass
# create the mutex locks
lock1 = Lock()
lock2 = Lock()
# create and configure the new threads
thread1 = Thread(target=task, args=(1, lock1, lock2))
thread2 = Thread(target=task, args=(2, lock2, lock1))
# start the new threads
thread1.start()
thread2.start()
# wait for threads to exit...
thread1.join()
thread2.join()

運行這個例子首先創(chuàng)建了兩個鎖。然后兩個線程都被創(chuàng)建,主線程等待線程的終止。

第一個線程接收 lock1 和 lock2 作為參數(shù)。它獲得了鎖 1 并 sleep。

第二個線程接收 lock2 和 lock1 作為參數(shù)。它獲得了鎖 2 并 sleep。

第一個線程醒來并試圖獲取鎖 2,但它必須等待,因為它已經(jīng)被第二個線程獲取。第二個線程醒來并試圖獲取鎖 1,但它必須等待,因為它已經(jīng)被第一個線程獲取。

結(jié)果是一個死鎖:

Thread 1 acquiring lock 1...
Thread 2 acquiring lock 1...
Thread 1 acquiring lock 2...
Thread 2 acquiring lock 2...

解決辦法是確保鎖在整個程序中總是以相同的順序獲得。這就是所謂的鎖排序。

模擬死鎖4:鎖未釋放

導(dǎo)致死鎖的另一個常見原因是線程未能釋放一個資源。這通常是由線程在關(guān)鍵部分引發(fā)錯誤或異常造成的,這種方式會阻止線程釋放資源,包括:

  • 未能釋放一個鎖
  • 未能釋放一個信號器
  • 未能到達一個 barrier
  • 未能在一個條件上通知線程
  • 未能設(shè)置一個事件
# example of a deadlock caused by a thread failing to release a lock
from time import sleep
from threading import Thread
from threading import Lock
# task to be executed in a new thread
def task(lock):
    # acquire the lock
    print('Thread acquiring lock...')
    lock.acquire()
    # fail
    raise Exception('Something bad happened')
    # release the lock (never gets here)
    print('Thread releasing lock...')
    lock.release()
# create the mutex lock
lock = Lock()
# create and configure the new thread
thread = Thread(target=task, args=(lock,))
# start the new thread
thread.start()
# wait a while
sleep(1)
# acquire the lock
print('Main acquiring lock...')
lock.acquire()
# do something...
# release lock (never gets here)
lock.release()

運行該例子時,首先創(chuàng)建鎖,然后創(chuàng)建并啟動新的線程。然后主線程阻塞。新線程運行。它首先獲得了鎖,然后引發(fā)了一個異常而失敗。該線程解開了鎖,但卻沒有解開鎖的代碼。新的線程終止了。最后,主線程被喚醒,然后試圖獲取鎖。由于鎖沒有被釋放,主線程永遠阻塞,導(dǎo)致了死鎖。

Thread acquiring lock...
Exception in thread Thread-1:
Traceback (most recent call last):
  ...
Exception: Something bad happened
Main acquiring lock...

總結(jié)

本文首先通過實際案例中可能出現(xiàn)死鎖的情況,介紹了死鎖的概念及條件,并通過 Python 代碼模擬死鎖的四種情況,更多關(guān)于Python 模擬死鎖的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • scrapy實踐之翻頁爬取的實現(xiàn)

    scrapy實踐之翻頁爬取的實現(xiàn)

    這篇文章主要介紹了scrapy實踐之翻頁爬取的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Python OpenCV一個窗口中顯示多幅圖像

    Python OpenCV一個窗口中顯示多幅圖像

    大家好,本篇文章主要講的是Python OpenCV一個窗口中顯示多幅圖像,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • Go/Python/Erlang編程語言對比分析及示例代碼

    Go/Python/Erlang編程語言對比分析及示例代碼

    這篇文章主要介紹了Go/Python/Erlang編程語言對比分析及示例代碼,本文重點是給大家介紹go語言,從語言對比分析的角度切入介紹,需要的朋友可以參考下
    2018-04-04
  • 如何使用Python讀取.xlsx指定行列

    如何使用Python讀取.xlsx指定行列

    讀取xlsx的整列數(shù)據(jù)很容易,但是要讀取指定sheet的指定列,這個相信大多數(shù)程序員都不會,下面這篇文章主要給大家介紹了關(guān)于如何使用Python讀取.xlsx指定行列的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • 解決Python3.8用pip安裝turtle-0.0.2出現(xiàn)錯誤問題

    解決Python3.8用pip安裝turtle-0.0.2出現(xiàn)錯誤問題

    turtle庫是python的基礎(chǔ)繪圖庫,這個庫被介紹為一個最常用的用來給孩子們介紹編程知識的方法庫,這篇文章主要介紹了解決Python3.8用pip安裝turtle-0.0.2出現(xiàn)錯誤問題,需要的朋友可以參考下
    2020-02-02
  • Python+OpenCV 實現(xiàn)簡單的高斯濾波(推薦)

    Python+OpenCV 實現(xiàn)簡單的高斯濾波(推薦)

    這篇文章主要介紹了Python+OpenCV 實現(xiàn)簡單的高斯濾波,在文中需要注意的是,這里我沒有特判當(dāng)sigma = 0的時候的情況,具體實現(xiàn)過程跟隨小編一起看看吧
    2021-09-09
  • Python給exe添加以管理員運行的屬性方法詳解

    Python給exe添加以管理員運行的屬性方法詳解

    這篇文章主要為大家介紹了Python給exe添加以管理員運行的屬性方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • 解決pytorch trainloader遇到的多進程問題

    解決pytorch trainloader遇到的多進程問題

    這篇文章主要介紹了解決pytorch trainloader遇到的多進程問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • 基于PyQt制作小紅書圖片抓取工具

    基于PyQt制作小紅書圖片抓取工具

    這篇文章主要為大家詳細介紹了如何基于PyQt制作一個小紅書圖片抓取工具,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • jupyter閃退的解決方法及卸載與安裝教程

    jupyter閃退的解決方法及卸載與安裝教程

    如果Anaconda的Jupyter Notebook無法打開并且頻繁閃退,可能是由于一些配置問題或者軟件沖突引起的,本文就來介紹一下jupyter閃退的解決方法及卸載與安裝教程,感興趣的可以了解一下
    2023-11-11

最新評論