监视器如何实施互斥?

时间:2014-07-08 13:38:20

标签: operating-system mutual-exclusion

所以我读到监视器强制互斥,主要是使用2个过程wait()和notify()/ signal(),我也理解使用监视器解决的问题。

我想知道的是监视器如何实施互斥?在wait()和notify()中会发生什么?

为什么只有1个线程可以调用监控程序?这个约束是如何实施的?

(如果我不是很清楚,我很抱歉)

2 个答案:

答案 0 :(得分:0)

假设监视器 lock ,并将您的房屋作为资源。现在,每当线程想要进入你的房子时,它必须首先获得你家的锁。只要该线程持有锁,就没有其他线程可以进入该房屋(访问该资源)。

wait() - >当前持有锁的线程,释放该对象/资源上的锁,并等待其他线程notify(),以便它可以继续执行。

notify() - >当前线程通知它正在释放该资源上的锁。等待此锁定的所有线程都会收到通知。操作系统非常智能,可以选择其中一个线程,为其提供锁定并执行它。

答案 1 :(得分:0)

监视器是两个线程基元的组合:互斥锁和条件变量。

互斥锁是一个可以被锁定的对象"一次只有一个线程。如果第二个线程试图锁定"互斥锁,第二个线程被迫等到第一个线程"解锁"互斥体。

有许多互斥体的快速实现,它们在没有内核帮助的情况下使用原子操作来锁定和解锁,但所有这些实现最终都要求操作系统为它们锁定互斥锁。操作系统会尝试为您锁定互斥锁,但如果已经锁定,操作系统将暂停该线程,并标记在解锁该特定互斥锁时应该重新唤醒它

条件变量是一个特别有趣的野兽,它解决了互斥体的一些特殊问题。特别是,等待互斥锁是不可中断的。一旦开始,您就可以等待互斥锁可用。你不能用例如异常中断互斥锁等待。 Mutexes只是放弃了这种能力以换取原始速度(它们比其他线程原语快得多)。

此外,互斥体并不总是保证公平,并且一​​些算法确实在不公平的等待时间方面遇到麻烦。使条件变量更公平更容易,因为它不必具有相同的运行时要求。

当然,尝试在使用互斥锁时很好地解决这些问题是很棘手的。条件变量具有以下功能

  • 等待(Mutex m) - 自动解锁' m'并开始等待互斥锁。醒来后,它将重新获得' m'
  • notify() - 唤醒正在等待条件变量的一个线程
  • broadcast() - 唤醒正在等待条件变量的每个线程。

等待是最奇怪的功能。为什么必须以这种方式定义它远远超出了问题的范围。

使用这些原语,可以对监视器进行伪代码

class Monitor
{
    private Mutex mutex;
    private ConditionVariable cond;
    public Monitor()
    {
        mutex = new Mutex();
        cond = new ConditionVariable();
    }

    public void enter()
    {
        mutex.lock();
    }


    public void exit()
    {
        mutex.unlock();
    }

    public void wait()
    {
        // mutex should be locked already
        cond.wait(mutex); // wait unlocks the mutex while waiting, relocks after waiting
    }

    public void notify()
    {
        cond.notify();
    }

    public void broadcast()
    {
        cond.broadcast();
    }
};

关于您感兴趣的特定功能:

  • 线程持有锁时调用wait()。在操作系统级别,它将当前线程添加到线程队列以在条件变量上唤醒,释放锁定,并告诉调度程序将线程置于休眠状态(所有三个都发生在原子上,#34;#34 ;意思是操作系统不会中途停止并切换到另一个线程)。当它被唤醒(通过通知或广播)时,它会在返回您的代码之前重新获取锁定。

  • notify()查看队列中的第一个线程,将其从队列中删除,并告诉OS调度程序再次开始调度该线程。

  • 互斥锁定通常使用名为"比较/交换"的特殊操作来完成。 (或其中一个变体)。比较交换在"原子值":

    上执行以下算法
    • 用户传入"比较"价值和交换价值。"
    • CPU比较"比较"值与原子值的实际值相对应。如果它们相同,则将原子值设置为"交换值"。如果它们不同,它什么都不做
    • 返回旧值(因此称为交换的原因)
    • 所有这些操作都是以原子方式完成的,因此没有两个线程可以冲突。在CPU级别,这通常是通过强制值一直到主存储库,跳过缓存,并保持"总线锁定"在整个过程中保持高电平,以便其他处理器不会中断。

这些都是火热的方式。如果你想了解更多,我强烈推荐维基百科任何一个不熟悉的名词。线程页面实际上非常好。 (Mutex,Condition Variable,Sleeping Barber和Compare Exchange都是很好的搜索)