2

我有一个简单的生产者/消费者场景,其中只有一个项目正在生产/消费。此外,生产者在继续之前等待工作线程完成。我意识到这种方式消除了多线程的全部意义,但请假设它确实需要这样(:

这段代码无法编译,但我希望你能明白:

// m_data is initially null

// This could be called by any number of producer threads simultaneously
void SetData(object foo)
{
    lock(x)                      // Line A
    {
        assert(m_data == null);
        m_data = foo;
        Monitor.Pulse(x)         // Line B
        while(m_data != null)
            Monitor.Wait(x)      // Line C
    }
}

// This is only ever called by a single worker thread
void UseData()
{
    lock(x)                      // Line D
    {
        while(m_data == null)
            Monitor.Wait(x)      // Line E
        // here, do something with m_data
        m_data = null;
        Monitor.Pulse(x)         // Line F
    }
}

这是我不确定的情况:

假设许多线程使用不同的输入调用 SetData()。其中只有一个会进入锁内,其余的将在 A 行被阻塞。假设进入锁内的那个设置m_data并进入 C 行。

问题:C 行上的 Wait() 是否允许 A 行上的另一个线程在工作线程到达之前获得锁并覆盖m_data ?

假设这没有发生,并且工作线程处理原始m_data并最终到达 F 行,那么当 Pulse() 关闭时会发生什么?

只有在 C 行等待的线程才能获得锁吗?或者它是否会与在线 A 上等待的所有其他线程竞争?

本质上,我想知道 Pulse()/Wait() 是否特别“在后台”相互通信,或者它们是否与 lock() 处于同一级别。

这些问题的解决方案(如果存在)当然是显而易见的——只需用另一个锁包围 SetData()——比如 lock(y)。我只是好奇这是否甚至是一个问题。

4

1 回答 1

10

不能保证消费者将在另一个生产者之前排队等待或就绪队列。
C# 和 Java 样式的监视器在Wikipedia 上的“隐式条件监视器”下进行了描述。
一个很好的概述Monitor(取自这个优秀的网站): 替代文字

“C 行上的 Wait() 是否允许 A 行的另一个线程在工作线程到达之前获得锁并覆盖 m_data?”

假设SetData()由两个生产者线程P1P2调用。
消费者线程C1也被启动。
P1P2C1都进入就绪队列。
P1首先获取锁。
等待队列为空,Pulse()online B无效。
P1等待line C,所以它被放置在等待队列中。
就绪队列中的下一个线程获取锁。
它同样可以是P2C1 - 在第一种情况下,断言失败。
你有一个竞争条件。

“假设这没有发生,并且工作线程处理原始 m_data,并最终进入 F 行,那么当 Pulse() 关闭时会发生什么?”

它将等待队列中的服务员移动到就绪队列。
锁由发出Pulse().
在脉冲线程释放锁后,被通知的线程将有机会获得锁(就绪队列中可能已经有其他线程)。
来自MSDN,Monitor.Pulse()
“当前拥有指定对象上的锁的线程调用此方法来通知下一个线程排队等待锁。收到脉冲后,等待线程被移动到就绪队列。当调用 Pulse 的线程释放锁时,就绪队列中的下一个线程(不一定是被脉冲的线程)获取锁。”

“只有在 C 线上等待的线程才能获得锁吗?还是它会与在 A 线上等待的所有其他线程竞争?”

就绪队列中的所有线程“竞争”下一个锁。
无论他们是直接到达那里还是通过等待队列到达那里都没有关系Pulse()

“队列”可以通过其他方式实现。(不是队列数据结构本身)。
这样,Monitor实现可能无法保证公平 - 但可能具有更高的整体吞吐量/性能。

于 2010-04-13T17:28:29.933 回答