![](/img/trans.png)
[英]Other thread does not wake up even after notifying and releasing the lock in python
[英]Python Thread not releasing lock
在 Python 中使用锁创建简单的消费者-生产者线程结构时,我遇到了一些问题,导致程序创建了意外的 output。
import threading
from time import sleep
import random
class AI:
def __init__(self):
self.a = None
self.mainLock = threading.Lock()
threading.Thread(target=self.producer,daemon=True).start()
threading.Thread(target=self.consumer,daemon=True).start()
def producer(self):
while True:
self.mainLock.acquire()
sleep(1)
temp = random.randint(-1,10)
print(f"Produced {temp}")
self.a = temp
self.mainLock.release()
def consumer(self):
while True:
self.mainLock.acquire()
if(self.a and self.a>0):
sleep(1.5)
print(f"Consumed {self.a}")
self.a = None
self.mainLock.release()
a = AI()
input()
Output -
Produced 0
Produced 8
Produced 9
Produced 1
Produced 9
Produced 10
Produced 5
Produced 1
这显然不是这里所期望的。 预期的 output 将包含一个又一个生产者的消费者。 但是,如果我在释放生产者内部的锁之后添加任何语句,那么代码运行良好,因为它预期会运行。
代码 -
import threading
from time import sleep
import random
class AI:
def __init__(self):
self.a = None
self.mainLock = threading.Lock()
threading.Thread(target=self.producer,daemon=True).start()
threading.Thread(target=self.consumer,daemon=True).start()
def producer(self):
while True:
self.mainLock.acquire()
sleep(1)
temp = random.randint(-1,10)
print(f"Produced {temp}")
self.a = temp
self.mainLock.release()
## = Newly added line
########################
print("released")
########################
def consumer(self):
while True:
self.mainLock.acquire()
if(self.a and self.a>0):
sleep(1.5)
print(f"Consumed {self.a}")
self.a = None
self.mainLock.release()
a = AI()
input()
Output -
Produced 10
released
Consumed 10
Produced 7
released
Consumed 7
Produced 2
released
Consumed 2
即使我用 sleep 语句替换 print 语句,代码也可以工作,无论睡眠时间有多短,它仍然可以正常工作。
例子 -
sleep(0.0000000000000000000000000000000000000000000000000000000000000000000000001)
为什么会这样? 在消费者发布后,代码如何能够在没有任何打印或睡眠的情况下从消费者跳回到生产者,否则代码无法从生产者跳到消费者?
释放锁并不能自动确保其他等待的线程会立即获得锁。
这尤其是一种反模式:
while (True):
someLock.acquire()
...
someLock.relase()
问题是,线程在释放锁后所做的下一件事就是再次获取它。
想象一下你在浴室里,门锁着。 外面有人等着进来。你让他们等了好久,所以也许他们坐了下来。 突然,你打开门,走出去,对方还没来得及站起来,你又退了进去,又把门锁上了。
这大致就是您的程序中正在发生的事情。
锁有利于一件事,而且只适用于一件事:您可以使用它们来阻止两个线程同时使用相同的资源。 如果您尝试将它们用于任何其他目的(例如,控制线程执行某些操作的顺序),您将遇到困难。
在许多编程语言/库中,一个线程向另一个线程发出信号的最原始方式称为条件变量。 见https://docs.python.org/3/library/threading.html#condition-objects
我认为您在这两个代码示例中都没有权利期望任何特定的 output 因为您将获得的结果将取决于线程如何以及何时获得对它们共享的处理器的控制权。 你所拥有的是一个竞争条件,定义如下:
当计算机程序要正确运行取决于程序进程或线程的顺序或时间时,软件中就会出现竞争条件。
在某些时间点,两个线程都没有持有锁,这显然是两个线程都启动时的情况。 查看producer
,如果它是第一个运行的,它会获取锁,打印它的消息,然后释放它的锁。 但可以肯定的是,可以立即返回循环顶部并再次重新获取锁。 同时,主线程最终可能会被调度并终止,然后作为守护线程的producer
和consumer
线程也将终止,而consumer
线程可能没有机会朗读(这是您所观察到的)。 但是,对producer
线程的任何更改,例如 I/O 语句(即print
)或sleep
,都可能导致 go 进入等待 state 并给consumer
线程一个运行的机会(您也观察到了)。 但即使在这种情况下,主线程也可能在consumer
线程可以打印任何内容之前被调度并终止。
一个线程释放互斥锁并不意味着另一个线程一定会获取它,在这种情况下,它可以再次被第一个线程获取。 对于大多数操作系统,此顺序不是确定性的。 第二个线程获取互斥锁的频率已经取决于操作系统线程的调度。
例如,在Windows
上,您的示例通常有效(生产,消费交错)。 在 Linux(我假设你会使用它)上,第二个线程正在挨饿。 仍然需要在release
之后添加命令,例如sleep
或使用其他方法来实现producer-consumer
模式,例如queue 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.