📌  相关文章
📜  门| Sudo GATE 2020 Mock I(2019 年 12 月 27 日)|第 41 题(1)

📅  最后修改于: 2023-12-03 15:28:47.075000             🧑  作者: Mango

Sudo GATE 2020 Mock I - Question 41

本题是关于加锁机制的,我们需要保证同一时刻只有一个线程可以访问共享资源。本题的要求是实现一个虚拟的门的类,如下:

class Gate:
    def enter(self):
        pass
        
    def exit(self):
        pass

在该类中,enter 方法将会检查当前门是打开还是关闭状态。如果门是关闭状态,则线程需要等待直到门打开,否则可以通过门进入。exit 方法被用于离开门的区域。在某个线程离开了门区域之后,该门可以被其他线程重新进入。

请实现上述的 Gate 类。

思路

在实现 Gate 类时,需要考虑门的两种状态:打开和关闭。如果门是关闭状态,线程需要等待直到门打开。在处理这种情况时,我们可以使用 Python 的内置 threading.Condition 类。该类允许线程等待某个条件,直到获得通知。我们使用一把锁来保护临界区域,然后使用条件变量来等待状态的改变。

为了实现 enterexit 方法,我们需要跟踪当前门的状态。我们可以使用布尔变量来存储门的状态,即 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 类来实现线程同步,从而保护了临界区域。在处理多线程程序时,正确的锁和同步机制是非常重要的。本题提供了一个很好的例子,展示了如何使用条件变量来保护共享资源。