Python 简明教程
Python - Thread Deadlock
死锁可以描述为并发故障模式。它是程序中一个或多个线程等待永远不会发生的条件的情况。结果,该线程无法继续进行,程序将卡住或冻结,且必须手动终止。
A deadlock may be described as a concurrency failure mode. It is a situation in a program where one or more threads wait for a condition that never occurs. As a result, the threads are unable to progress and the program is stuck or frozen and must be terminated manually.
在并发程序中,可能会以多种方式出现死锁情况。死锁永远不会有意地发展,相反,它们实际上是代码中的副作用或错误。
Deadlock situation may arise in many ways in your concurrent program. Deadlocks are never not developed intentionally, instead, they are in fact a side effect or bug in the code.
下面列出了线程死锁的常见原因 −
Common causes of thread deadlocks are listed below −
-
A thread that attempts to acquire the same mutex lock twice.
-
Threads that wait on each other (e.g. A waits on B, B waits on A).
-
When a thread that fails to release a resource such as lock, semaphore, condition, event, etc.
-
Threads that acquire mutex locks in different orders (e.g. fail to perform lock ordering).
How to Avoid Deadlocks in Python Threads
当多线程应用程序中的多个线程尝试访问同一资源时,例如对同一文件执行读/写操作,它会导致数据不一致。因此,使用 locking mechanisms 同步对资源的并发访问非常重要。
When multiple threads in a multi-threaded application attempt to access the same resource, such as performing read/write operations on the same file, it can lead to data inconsistency. Therefore, it is important to synchronize concurrent access to resources by using locking mechanisms.
Python threading 模块提供了一种简单的锁定机制来 synchronize threads 。你可以通过调用 Lock() 类来创建一个新的锁定对象,它将锁定初始化为解锁状态。
The Python threading module provides a simple-to-implement locking mechanism to synchronize threads. You can create a new lock object by calling the Lock() class, which initializes the lock in an unlocked state.
Locking Mechanism with the Lock Object
一个 Lock 类的对象有两个可能状态——锁定或解锁,创建时最初为解锁状态。锁定不属于任何特定线程。
An object of the Lock class has two possible states − locked or unlocked, initially in unlocked state when first created. A lock doesn’t belong to any particular thread.
锁定类定义了 acquire() 和 release() 方法。
The Lock class defines acquire() and release() methods.
The acquire() Method
锁定类的 acquire() 方法将锁定的状态从解锁改为锁定。它立即返回,除非可选的锁定参数被设置为 True,在这种情况下,它将一直等到获得锁定。
The acquire() method of the Lock class changes the lock’s state from unlocked to locked. It returns immediately unless the optional blocking argument is set to True, in which case it waits until the lock is acquired.
以下是这个方法的 Syntax −
Here is the Syntax of this method −
Lock.acquire(blocking, timeout)
其中,
Where,
-
blocking − If set to False, it means do not block. If a call with blocking set to True would block, return False immediately; otherwise, set the lock to locked and return True.
-
timeout − Specifies a timeout period for acquiring the lock.
这个方法的返回值如果成功获取锁定,则为 True;如果失败,则为 False。
The return value of this method is True if the lock is acquired successfully; False if not.
The release() Method
当状态被锁定时,另一个线程中的这个方法会将其改为解锁。它可以被任何线程调用,不仅限于获取锁定的线程
When the state is locked, this method in another thread changes it to unlocked. This can be called from any thread, not only the thread which has acquired the lock
以下是 release() 方法的 Syntax −
Following is the Syntax of the release() method −
Lock.release()
release() 方法只能在锁定状态下调用。如果尝试释放一个未锁定的锁定,会引发 RuntimeError。
The release() method should only be called in the locked state. If an attempt is made to release an unlocked lock, a RuntimeError will be raised.
当锁定被锁定时,将它重置为解锁,并返回。如果有任何其他线程因为等待锁定被解锁而锁定,只允许其中一个继续。这个方法没有返回值。
When the lock is locked, reset it to unlocked, and return. If any other threads are blocked waiting for the lock to become unlocked, allow exactly one of them to proceed. There is no return value of this method.
Example
在以下程序中,有两个线程尝试调用 synchronized() 方法。其中一个线程获取锁定并获得访问权,而另一个线程则等待。当第一个线程的 run() 方法完成时,锁定被释放,并且 synchronized 方法对第二个线程可用。
In the following program, two threads try to call the synchronized() method. One of them acquires the lock and gains the access while the other waits. When the run() method is completed for the first thread, the lock is released and the synchronized method is available for second thread.
当两个线程都加入时,程序结束。
When both the threads join, the program comes to an end.
from threading import Thread, Lock
import time
lock=Lock()
threads=[]
class myThread(Thread):
def __init__(self,name):
Thread.__init__(self)
self.name=name
def run(self):
lock.acquire()
synchronized(self.name)
lock.release()
def synchronized(threadName):
print ("{} has acquired lock and is running synchronized method".format(threadName))
counter=5
while counter:
print ('**', end='')
time.sleep(2)
counter=counter-1
print('\nlock released for', threadName)
t1=myThread('Thread1')
t2=myThread('Thread2')
t1.start()
threads.append(t1)
t2.start()
threads.append(t2)
for t in threads:
t.join()
print ("end of main thread")
它将生成以下 output −
It will produce the following output −
Thread1 has acquired lock and is running synchronized method
**********
lock released for Thread1
Thread2 has acquired lock and is running synchronized method
**********
lock released for Thread2
end of main thread
Semaphore Object for Synchronization
除了锁定之外,Python threading 模块还支持 semaphores ,它提供了另一种同步技术。它是著名计算机科学家 Edsger W. Dijkstra 发明的最古老的同步技术之一。
In addition to locks, Python threading module supports semaphores, which offering another synchronization technique. It is one of the oldest synchronization techniques invented by a well-known computer scientist, Edsger W. Dijkstra.
信号量的基本概念是使用一个由每次 acquire() 调用递减,且每次 release() 调用递增的内部计数器。计数器永远不会低于零;当 acquire() 发现它是零时,它将锁定,一直等到其他线程调用 release() 为止。
The basic concept of semaphore is to use an internal counter which is decremented by each acquire() call and incremented by each release() call. The counter can never go below zero; when acquire() finds that it is zero, it blocks, waiting until some other thread calls release().
线程模块中的信号量类定义了 acquire() 和 release() 方法。
The Semaphore class in threading module defines acquire() and release() methods.
The acquire() Method
如果输入时内部计数器大于零,将其减一并立即返回 True。
If the internal counter is larger than zero on entry, decrement it by one and return True immediately.
如果输入时内部计数器为零,则锁定,直到因调用 release() 而被唤醒。一旦唤醒(并且计数器大于 0),将计数器减 1 并返回 True。每次调用 release(),都将唤醒一个线程。唤醒线程的顺序是任意的。
If the internal counter is zero on entry, block until awoken by a call to release(). Once awoken (and the counter is greater than 0), decrement the counter by 1 and return True. Exactly one thread will be awoken by each call to release(). The order in which threads awake is arbitrary.
如果阻塞参数被设置为 False,不要阻塞。如果未带参数的调用将阻塞,立即返回 False;否则,执行与未带参数调用时相同的操作,并返回 True。
If blocking parameter is set to False, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.
The release() Method
释放一个信号量,将内部计数器加 1。当在进入时它为零,并且其他线程正在等待它再次变为大于零时,唤醒其中 n 个线程。
Release a semaphore, incrementing the internal counter by 1. When it was zero on entry and other threads are waiting for it to become larger than zero again, wake up n of those threads.
Example
此示例演示了如何在 Python 中使用 ^ 对象来控制多个线程对共享资源的访问,以避免 Python 多线程程序中的死锁。
This example demonstrates how to use a Semaphore object in Python to control access to a shared resource among multiple threads, for avoiding deadlock in Python’s multi-threaded program.
from threading import *
import time
# creating thread instance where count = 3
lock = Semaphore(4)
# creating instance
def synchronized(name):
# calling acquire method
lock.acquire()
for n in range(3):
print('Hello! ', end = '')
time.sleep(1)
print( name)
# calling release method
lock.release()
# creating multiple thread
thread_1 = Thread(target = synchronized , args = ('Thread 1',))
thread_2 = Thread(target = synchronized , args = ('Thread 2',))
thread_3 = Thread(target = synchronized , args = ('Thread 3',))
# calling the threads
thread_1.start()
thread_2.start()
thread_3.start()
它将生成以下 output −
It will produce the following output −
Hello! Hello! Hello! Thread 1
Hello! Thread 2
Thread 3
Hello! Hello! Thread 1
Hello! Thread 3
Thread 2
Hello! Hello! Thread 1
Thread 3
Thread 2