Python 线程死锁

Python 线程死锁

死锁可以描述为一种并发故障模式。它是程序中的一种情况,其中一个或多个线程等待从未发生的条件。结果,线程无法继续执行,程序被卡住或冻结,必须手动终止。

并发程序中可能以许多方式出现死锁情况。死锁从未被有意开发,而是实际上是代码中的副作用或错误。

线程死锁的常见原因如下:

  • 尝试两次获取相同的互斥锁的线程。

  • 互相等待的线程(例如A等待B,B等待A)。

  • 线程未释放资源(例如锁,信号量,条件,事件等)。

  • 以不同的顺序获取互斥锁的线程(例如无法执行锁排序)。

如果多个多线程应用程序中的线程尝试访问同一资源,例如对同一文件执行读写操作,可能会导致数据不一致。因此,重要的是使用同步方式处理并发,以便当一个线程使用资源时,它对其他线程进行了锁定。

Python提供的线程模块包括一种简单易实现的锁定机制,可以用来同步线程。通过调用Lock()方法创建一个新的锁,该方法返回新的锁。

锁对象

Lock类的对象有两种可能的状态 – 锁定或解锁,初始状态为解锁。锁不属于任何特定线程。

Lock类定义了acquire()和release()方法。

acquire()方法

当状态为解锁时,此方法将状态更改为锁定并立即返回。该方法接受一个可选的阻塞参数。

语法

Lock.acquire(blocking, timeout)

参数

  • blocking − 如果设置为False,则表示不阻塞。如果将blocking设置为True的调用将会阻塞,立即返回False;否则,将锁定设置为已锁定,并返回True。

此方法的返回值为True表示成功获取锁;False表示未获取锁。

release()方法

当状态为已锁定时,另一个线程中的此方法将其状态更改为未锁定。此方法可以从任何线程调用,不仅限于已获取锁的线程。

语法

Lock.release()

release()方法只应在锁定状态下调用。如果尝试释放未锁定的锁,则会引发RuntimeError。

当锁被锁定时,将其重置为未锁定状态并返回。如果有其他线程阻塞等待锁变为未锁定状态,则只允许其中的一个线程继续执行。该方法没有返回值。

示例

在下面的程序中,两个线程尝试调用synchronized()方法。其中一个线程获得了锁并获得了访问权限,而另一个线程则等待。当第一个线程的run()方法完成时,锁被释放,第二个线程可以访问synchronized方法。

当两个线程都加入时,程序结束。

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")

它将生成以下 输出

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

信号量对象

Python使用另一种称为信号量的机制来支持线程同步 semaphore 。这是一种由著名计算机科学家Edsger W. Dijkstra发明的最古老的同步技术之一。

信号量的基本概念是使用一个内部计数器,每个acquire()调用将其递减,每个release()调用将其递增。计数器永远不能低于零;当acquire()发现计数器为零时,它会阻塞,直到某个其他线程调用release()。

线程模块中的Semaphore类定义了acquire()和release()方法。

acquire()方法

如果内部计数器在进入时大于零,则将其减一并立即返回True。

如果内部计数器在进入时为零,则阻塞直到通过调用release()唤醒。一旦唤醒(且计数器大于0),将计数器减一并返回True。每次调用release()都会唤醒一个线程。唤醒线程的顺序是任意的。

如果将blocking参数设置为False,则不阻塞。如果不带参数的调用会阻塞,则立即返回False;否则,执行与不带参数调用相同的操作,并返回True。

release()方法

释放信号量,将内部计数器增加1。如果在进入时计数器为零且其他线程正在等待计数器再次大于零,则唤醒其中的n个线程。

示例

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()

它将生成以下 输出

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

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程