我正在尝试解决这个 LeetCode 问题,Print Zero Even Odd :
我尝试使用 threading.Condition
执行以下解决方案对象:
import threading
from typing import Callable, Optional
class ZeroEvenOdd:
def __init__(self, n: int):
self.n = n
self.i = 0
self.last_printed: Optional[int] = None
self.condition = threading.Condition()
def zero(self, printNumber: Callable[[int], None]) -> None:
with self.condition:
self.condition.wait_for(lambda: self.last_printed is None or self.last_printed > 0)
if self.done:
return
printNumber(0)
self.last_printed = 0
self.i += 1
self.condition.notify_all()
def even(self, printNumber: Callable[[int], None]) -> None:
with self.condition:
self.condition.wait_for(lambda: self.last_printed == 0 and self.i % 2 == 0)
if self.done:
return
self._print_and_notify()
def odd(self, printNumber: Callable[[int], None]) -> None:
with self.condition:
self.condition.wait_for(lambda: self.last_printed == 0 and self.i % 2 == 1)
if self.done:
return
self._print_and_notify()
def _print_and_notify(self) -> None:
printNumber(self.i)
self.last_printed = self.i
self.condition.notify_all()
@property
def done(self) -> bool:
if self.last_printed is not None and self.last_printed >= self.n:
self.condition.release()
self.condition.notify_all()
return True
return False
def printNumber(x: int) -> None:
print(x)
zero_even_odd = ZeroEvenOdd(n=5)
threadA = threading.Thread(target=zero_even_odd.zero, args=(printNumber,))
threadB = threading.Thread(target=zero_even_odd.even, args=(printNumber,))
threadC = threading.Thread(target=zero_even_odd.odd, args=(printNumber,))
if __name__ == "__main__":
threadA.start()
threadB.start()
threadC.start()
但是,当我运行它时,我发现它打印 0
,然后打印 1
,然后无限期挂起:
> python print_zero_even_odd.py
0
1
^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1308, in _shutdown
lock.acquire()
KeyboardInterrupt
我有点困惑为什么在第一次调用 odd()
后,zero()
不再被调用。毕竟,在 odd()
中打印数字 1
后,self.last_printed
被设置为 1
,这应触发 zero()
方法的 wait_for()
条件,self.last_printed > 0
。
知道为什么这个程序没有按预期工作吗?
最佳答案
正如 Sraw 所指出的,我的代码中没有循环,因此没有理由期望再次调用 zero()
。我最终使用 threading.Lock
对象以不同的方式解决了这个问题:
import threading
from typing import Callable, Optional
class ZeroEvenOdd:
def __init__(self, n: int):
self.n = n
self.i = 0
self.lock_zero = threading.Lock()
self.lock_odd = threading.Lock()
self.lock_even = threading.Lock()
self.lock_odd.acquire()
self.lock_even.acquire()
def zero(self, printNumber: Callable[[int], None]) -> None:
for _ in range(self.n):
self.lock_zero.acquire()
printNumber(0)
if self.i % 2 == 0:
self.lock_odd.release()
else:
self.lock_even.release()
def even(self, printNumber: Callable[[int], None]) -> None:
for i in range(2, self.n + 1, 2):
self.lock_even.acquire()
printNumber(i)
self.i = i
self.lock_zero.release()
def odd(self, printNumber: Callable[[int], None]) -> None:
for i in range(1, self.n + 1, 2):
self.lock_odd.acquire()
printNumber(i)
self.i = i
self.lock_zero.release()
def printNumber(x: int) -> None:
print(x)
zero_even_odd = ZeroEvenOdd(n=5)
threadA = threading.Thread(target=zero_even_odd.zero, args=(printNumber,))
threadB = threading.Thread(target=zero_even_odd.even, args=(printNumber,))
threadC = threading.Thread(target=zero_even_odd.odd, args=(printNumber,))
if __name__ == "__main__":
threadA.start()
threadB.start()
threadC.start()
这将打印所需的输出:
> python print_zero_even_odd.py
0
1
0
2
0
3
0
4
0
5
并以相当快速且节省内存的方式解决 LeetCode 问题:
关于Python threading.Condition.notify_all() 不触发下一个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57550973/