Python 官方文档:入门教程 => 点击学习
目录前言模拟死锁1:线程等待本身模拟死锁2:线程互相等待模拟死锁3:以错误的顺序获取锁模拟死锁4:锁未释放总结前言 常见的例子是在银行账户上:假如要在两个银行账户之间执行交易,你必须
常见的例子是在银行账户上:假如要在两个银行账户之间执行交易,你必须确保两个账户都被锁定,不受其他交易的影响,以达到正确的资金转移量。在这里,这个类比并不完全成立--哲学家对应的是锁定账户的交易(分叉)--但同样的技术困难也会出现。
其他的例子包括电商秒杀系统,多个用户抢一个商品,不允许一个数据库被多个客户同时修改。
死锁也是由一个并发程序需要同时具备的条件来定义的,这样才会发生死锁。这些条件是由计算机科学家Edward G. Coffman, Jr .首先提出的,因此被称为 Coffman 条件。这些条件如下:
造成线程死锁的常见例子包括:
导致死锁的一个常见原因是线程在自己身上等待。
我们并不打算让这种死锁发生,例如,我们不会故意写代码,导致线程自己等待。相反,由于一系列的函数调用和变量的传递,这种情况会意外地发生。
一个线程可能会因为很多原因而在自己身上等待,比如:
开发一个 task()
函数,直接尝试两次获取同一个 mutex 锁。也就是说,该任务将获取锁,然后再次尝试获取锁。
# 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
这将导致死锁,因为线程已经持有该锁,并将永远等待自己释放该锁,以便它能再次获得该锁, task()
试图两次获取同一个锁并触发死锁。
在主线程中,可以创建锁:
# create the mutex lock
lock = Lock()
然后我们将创建并配置一个新的线程,在一个新的线程中执行我们的 task()
函数,然后启动这个线程并等待它终止,而它永远不会终止。
# 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()
运行结果如下:
首先创建锁,然后新的线程被混淆并启动,主线程阻塞,直到新线程终止,但它从未这样做。
新线程运行并首先获得了锁。然后它试图再次获得相同的互斥锁并阻塞。
它将永远阻塞,等待锁被释放。该锁不能被释放,因为该线程已经持有该锁。因此,该线程已经陷入死锁。
该程序必须被强制终止,例如,通过 Control-C 杀死终端。
一个常见的例子就是两个或多个线程互相等待。例如:线程 A 等待线程 B,线程 B 等待线程 A。
如果有三个线程,可能会出现线程循环等待,例如:
如果你设置了线程来等待其他线程的结果,这种死锁是很常见的,比如在一个流水线或工作流中,子任务的一些依赖关系是不符合顺序的。
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
,然后创建一个新的线程 new_thread
,并调用传递给主线程的 task()
函数。新线程返回一条信息并等待主线程停止,主线程用新线程的实例调用 task()
函数,并等待新线程的终止。每个线程都在等待另一个线程终止,然后自己才能终止,这导致了一个死锁。
运行结果:
[Thread-1] waiting on [MainThread]...
[MainThread] waiting on [Thread-1]...
导致死锁的一个常见原因是,两个线程同时以不同的顺序获得锁。例如,我们可能有一个受锁保护的关键部分,在这个关键部分中,我们可能有代码或函数调用受第二个锁保护。
可能会遇到这样的情况:一个线程获得了锁 1 ,然后试图获得锁 2,然后有第二个线程调用获得锁 2 的功能,然后试图获得锁 1。如果这种情况同时发生,线程 1 持有锁 1,线程 2 持有锁 2,那么就会有一个死锁。
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()
运行这个例子首先创建了两个锁。然后两个线程都被创建,主线程等待线程的终止。
第一个线程接收 lock1 和 lock2 作为参数。它获得了锁 1 并 sleep。
第二个线程接收 lock2 和 lock1 作为参数。它获得了锁 2 并 sleep。
第一个线程醒来并试图获取锁 2,但它必须等待,因为它已经被第二个线程获取。第二个线程醒来并试图获取锁 1,但它必须等待,因为它已经被第一个线程获取。
结果是一个死锁:
Thread 1 acquiring lock 1...
Thread 2 acquiring lock 1...
Thread 1 acquiring lock 2...
Thread 2 acquiring lock 2...
解决办法是确保锁在整个程序中总是以相同的顺序获得。这就是所谓的锁排序。
导致死锁的另一个常见原因是线程未能释放一个资源。这通常是由线程在关键部分引发错误或异常造成的,这种方式会阻止线程释放资源,包括:
# 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()
运行该例子时,首先创建锁,然后创建并启动新的线程。然后主线程阻塞。新线程运行。它首先获得了锁,然后引发了一个异常而失败。该线程解开了锁,但却没有解开锁的代码。新的线程终止了。最后,主线程被唤醒,然后试图获取锁。由于锁没有被释放,主线程永远阻塞,导致了死锁。
Thread acquiring lock...
Exception in thread Thread-1:
Traceback (most recent call last):
...
Exception: Something bad happened
Main acquiring lock...
本文首先通过实际案例中可能出现死锁的情况,介绍了死锁的概念及条件,并通过 python 代码模拟死锁的四种情况,更多关于Python 模拟死锁的资料请关注编程网其它相关文章!
--结束END--
本文标题: Python 模拟死锁的常见实例详解
本文链接: https://lsjlt.com/news/120245.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0