【问题标题】:Implementing locks and blocking threads实现锁和阻塞线程
【发布时间】:2013-01-22 22:43:50
【问题描述】:

如果一个线程试图获取一个锁并且它被占用了,它应该进入休眠状态并保持休眠状态直到锁被释放。如果一个线程释放了一个锁(它被创建它的线程占用),它应该唤醒线程。

我的问题是,如果我们唤醒锁定地址上的所有线程与锁定地址上的单个线程(锁定释放后),这有什么不同吗?如果我们要唤醒单个线程,那么唤醒哪个线程有意义,第一个在锁定地址上进入睡眠状态?

我可以看到唤醒一个线程的一些优势,如果我们唤醒所有线程,n-1 可能会重新进入睡眠状态。但是不知道单线程唤醒有没有坏处。

【问题讨论】:

  • 如果它是一个锁(互斥锁,临界区),唤醒多个线程等待它的意义何在?为什么“他们可能重新入睡”,而这似乎是唯一可能发生的事情?

标签: synchronization operating-system thread-safety locking block


【解决方案1】:

我相信您将 Windows 锁(互斥锁、临界区)与 .Net 监视器(在 C# 中可以使用关键字 lock 作为进入/退出块的语法糖)混淆了。

Windows 互斥锁和临界区只能进入和退出,即只有一个队列与之关联。队列中的所有线程都等待锁被释放,当这种情况发生时,队列中的下一个线程将控制锁并开始运行。这一切都是自动发生的。当前持有锁的线程与此无关,它不能选择唤醒一个或多个等待释放锁的其他线程。

.Net 的 Monitor 有两个队列:就绪队列和等待队列。

就绪队列的行为与 Win32 Mutex 或临界区队列完全相同,并使用 Enter/Leave 方法进行控制。

等待队列是使用 Wait/Pulse/PulseAll 方法控制的单独队列。这些方法只能由持有 Monitor 的线程调用。当线程调用 Wait 时,它会释放 Monitor 并进入等待队列。然后,不同的线程可以调用 Pulse 来移动一个线程,或者调用 Pulse All 来将所有线程从等待队列移动到就绪队列(记住调用 Pulse/PulseAll 的线程持有 Monitor。)

从计算机科学的角度来看,监视器是同步线程所需的单个原语(信号量、事件、互斥体、屏障等都可以用监视器来实现),从实际的角度来看,监视器用作互斥体和适用于需要由两个线程以锁步执行操作的情况。但大多数情况下,代码在使用事件时更具可读性。

进一步阅读:

Wikipedia page about Monitors for the historic/computer science aspect

MSDN Monitor class

【讨论】:

  • 由于 OP 没有在任何地方提到 .Net,我想知道您为什么从一开始就谈论 .Net 监视器。如果 OP 对监视器是特定于 .Net 的东西感到困惑,那将是一个遗憾,而它是一个通用概念,否则您可以很好地解释它。
  • @Pavel 你可能是对的。在我看来,这个问题是最有可能出现的错误。 .Net 监视器是唯一流行的使用“lock”关键字的同步原语,它可以唤醒其他线程。我可以编辑答案以明确 Monitor 是一个通用概念,与 .Net 无关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
  • 2017-11-22
相关资源
最近更新 更多