📜  门| GATE-IT-2004 |第 62 题(1)

📅  最后修改于: 2023-12-03 14:58:32.546000             🧑  作者: Mango

门 GATE-IT-2004 第 62 题

这是一道计算机科学领域的经典问题,通常称为“门问题”或者“The Gate Problem”(题目原文为“门道问题”)。该问题可以帮助程序员深入理解并发编程以及互斥访问的概念。

问题描述

考虑一个门,它可以同时容纳 k 个人通过。只要有人通过这扇门,它就会自动关闭并锁定 k 秒钟。在此期间,其他人必须等待,直到门重新开启。如果此时有多个人在门前等待,他们需要排成一排,等待最先到达门前的人通过后再逐一通过。

现在假设有 m 个人共同通过这扇门。你的任务是设计一个并发算法,使得这 m 个人能够同时通过这扇门,而且不会相互干扰,即在任何时刻,门内最多只会有 k 个人,门外最多只会有 m-k 个人。

解题思路

该问题需要考虑如何协同多个线程完成门的互斥访问。通过分析可以得到一个基本的思路:

  1. 设定一个计数器 count 来表示当前门内的人数;
  2. 当每个线程到达门前时,首先需要检查 count 是否小于 k。如果是,则允许当前线程进入,同时将 count 加一,否则需要等待;
  3. 当每个线程完成通过门的操作后,需要将 count 减一。

这样就能保证门内的人数最多为 k。但还需要考虑如何处理门外的人数限制,即在门内的人数达到 k 后,其他线程应该等待直到有人离开门才能通过。

为了处理这种情况,我们可以设置两个计数器:in_count 和 out_count,分别表示当前门内和门外的人数。进入门时,先等待 out_count 为 0,表示当前门外没有人;离开门时,对 out_count 进行操作,表示此时可以有其他人进入门。

需要注意的是,在等待时要使用条件变量,而不是忙等待。这是因为忙等待会占用大量的处理器资源,影响程序的性能。

代码实现

以下是该算法的一个简单实现。该实现使用了 C++11 的并发库,并且使用了条件变量和互斥量实现线程之间的同步和互斥。

    #include <iostream>
    #include <mutex>
    #include <condition_variable>
    #include <thread>

    using namespace std;

    const int k = 5;
    const int m = 20;

    mutex m_lock;
    condition_variable cond;
    int count = 0;
    int in_count = 0;
    int out_count = m;

    void through_gate(int id) {
        unique_lock<mutex> lk(m_lock);
        while (out_count > 0) { // Wait for no people outside.
            cond.wait(lk);
        }
        while (count >= k) {    // Wait for room inside.
            cond.wait(lk);
        }

        in_count++;             // Increase number in room.
        out_count++;            // Decrease number outside.
        count++;                // Increase room population.
        printf("Thread %d entered room! Current number: %d\n", id, count);

        this_thread::sleep_for(chrono::milliseconds(500));

        count--;                // Leave room.
        in_count--;             // Decrease number in room.
        out_count--;            // Increase number outside.
        printf("Thread %d left room. Current number: %d\n", id, count);

        cond.notify_all();      // Notify all waiting threads.
    }

    int main() {
        thread t[m];
        for (int i = 0; i < m; i++) {
            t[i] = thread(through_gate, i);
        }
        for (int i = 0; i < m; i++) {
            t[i].join();
        }
        return 0;
    }

在该实现中,通过互斥量和条件变量实现了线程之间的同步和互斥,同时也避免了忙等待的情况。在线程执行过程中需要等待时,调用 cond.wait() 阻塞线程,并且释放互斥量。当条件满足时,条件变量会自动唤醒等待的线程,并且重新获得互斥量。

此外,通过在调用 count 和 in_count 和 out_count 的时候进行互斥操作,实现了多线程间的数据安全。当多个线程同时修改这些共享变量时,会发生不可预期的错误。因此需要保证在修改期间对其进行互斥访问。

这个简单的实现类似于餐厅问题(Dining Philosophers Problem),是学习多线程编程的一个很好的例子。它展示了如何使用同步和互斥机制来解决并发问题,例如不安全地访问共享资源。它可以帮助开发人员更好地理解多线程编程的概念和技术。