📅  最后修改于: 2023-12-03 15:28:47.075000             🧑  作者: Mango
本题是关于加锁机制的,我们需要保证同一时刻只有一个线程可以访问共享资源。本题的要求是实现一个虚拟的门的类,如下:
class Gate:
def enter(self):
pass
def exit(self):
pass
在该类中,enter
方法将会检查当前门是打开还是关闭状态。如果门是关闭状态,则线程需要等待直到门打开,否则可以通过门进入。exit
方法被用于离开门的区域。在某个线程离开了门区域之后,该门可以被其他线程重新进入。
请实现上述的 Gate
类。
在实现 Gate
类时,需要考虑门的两种状态:打开和关闭。如果门是关闭状态,线程需要等待直到门打开。在处理这种情况时,我们可以使用 Python 的内置 threading.Condition
类。该类允许线程等待某个条件,直到获得通知。我们使用一把锁来保护临界区域,然后使用条件变量来等待状态的改变。
为了实现 enter
和 exit
方法,我们需要跟踪当前门的状态。我们可以使用布尔变量来存储门的状态,即 True
表示门是打开的,False
表示门是关闭的。当线程尝试进入一个关闭的门时,它将会被阻塞,直到门被打开。同样地,当线程离开门时,我们需要将门的状态设置为打开。
我们的 Gate
类可以通过以下方式实现:
import threading
class Gate:
def __init__(self):
self._open = True
self._lock = threading.Lock()
self._cv = threading.Condition(lock=self._lock)
def enter(self):
with self._lock:
while not self._open:
self._cv.wait()
self._open = False
def exit(self):
with self._lock:
self._open = True
self._cv.notify_all()
在上述实现中,我们首先创建了一个布尔变量 _open
来表示门的状态。我们还创建了一个锁 _lock
和一个条件变量 _cv
。注意我们传入了 _lock
作为参数,以便让 _cv
使用同一个锁来同步线程。
在 enter
方法中,我们首先获取了锁 _lock
。然后,我们检查当前门的状态,如果门是关闭的,我们使用 _cv.wait()
来等待条件的改变。当条件变量 _cv
被通知时,线程将被唤醒。在这种情况下,线程还必须重新确定门的状态,因为其他线程可能已经改变了门的状态。因此,我们使用 while not self._open
循环来检查门的状态,直到门被打开。一旦门打开,我们将 _open
设置为 False
,进而锁定门口。
在 exit
方法中,我们首先获取锁 _lock
。然后,我们将 _open
设置为 True
,表示门口已经开放。最后,我们使用 _cv.notify_all()
来通知其他线程门的状态已经改变,新的线程可以进入。这里使用 _cv.notify_all()
来通知所有等待线程,而不是只通知一个线程。这将确保所有线程都能立即获得通知,这样我们就可以降低线程异常的风险。
我们可以使用以下测试用例来确保 Gate
类的正确性:
import threading
import time
def worker(gate: Gate, name: str):
print(f"{name}正在等待门开……")
gate.enter()
print(f"{name}通过门进入了临界区域")
time.sleep(1)
print(f"{name}正在离开临界区域")
gate.exit()
print(f"{name}离开了门")
gate = Gate()
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(gate, f"线程{i}"))
threads.append(t)
t.start()
for t in threads:
t.join()
在上面的测试用例中,我们创建了 5 个线程,并尝试进入一个共享的临界区域。如果我们的 Gate
类正确实现,那么我们应该看到每个线程顺序地进入和离开这个区域。
在本题中,我们实现了一个虚拟的门的类,并使用了 Python 的内置 Condition
类来实现线程同步,从而保护了临界区域。在处理多线程程序时,正确的锁和同步机制是非常重要的。本题提供了一个很好的例子,展示了如何使用条件变量来保护共享资源。