所以我读到监视器强制互斥,主要是使用2个过程wait()和notify()/ signal(),我也理解使用监视器解决的问题。
我想知道的是监视器如何实施互斥?在wait()和notify()中会发生什么?
为什么只有1个线程可以调用监控程序?这个约束是如何实施的?
(如果我不是很清楚,我很抱歉)
答案 0 :(得分:0)
假设监视器是 lock ,并将您的房屋作为资源。现在,每当线程想要进入你的房子时,它必须首先获得你家的锁。只要该线程持有锁,就没有其他线程可以进入该房屋(访问该资源)。
wait()
- >当前持有锁的线程,释放该对象/资源上的锁,并等待其他线程notify()
,以便它可以继续执行。
notify()
- >当前线程通知它正在释放该资源上的锁。等待此锁定的所有线程都会收到通知。操作系统非常智能,可以选择其中一个线程,为其提供锁定并执行它。
答案 1 :(得分:0)
监视器是两个线程基元的组合:互斥锁和条件变量。
互斥锁是一个可以被锁定的对象"一次只有一个线程。如果第二个线程试图锁定"互斥锁,第二个线程被迫等到第一个线程"解锁"互斥体。
有许多互斥体的快速实现,它们在没有内核帮助的情况下使用原子操作来锁定和解锁,但所有这些实现最终都要求操作系统为它们锁定互斥锁。操作系统会尝试为您锁定互斥锁,但如果已经锁定,操作系统将暂停该线程,并标记在解锁该特定互斥锁时应该重新唤醒它
条件变量是一个特别有趣的野兽,它解决了互斥体的一些特殊问题。特别是,等待互斥锁是不可中断的。一旦开始,您就可以等待互斥锁可用。你不能用例如异常中断互斥锁等待。 Mutexes只是放弃了这种能力以换取原始速度(它们比其他线程原语快得多)。
此外,互斥体并不总是保证公平,并且一些算法确实在不公平的等待时间方面遇到麻烦。使条件变量更公平更容易,因为它不必具有相同的运行时要求。
当然,尝试在使用互斥锁时很好地解决这些问题是很棘手的。条件变量具有以下功能
等待是最奇怪的功能。为什么必须以这种方式定义它远远超出了问题的范围。
使用这些原语,可以对监视器进行伪代码
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调度程序再次开始调度该线程。
互斥锁定通常使用名为"比较/交换"的特殊操作来完成。 (或其中一个变体)。比较交换在"原子值":
上执行以下算法这些都是火热的方式。如果你想了解更多,我强烈推荐维基百科任何一个不熟悉的名词。线程页面实际上非常好。 (Mutex,Condition Variable,Sleeping Barber和Compare Exchange都是很好的搜索)