gpt4 book ai didi

c# - Monitor.Pulse 和 Monitor.Wait 之间的竞争条件?

转载 作者:太空狗 更新时间:2023-10-30 01:20:14 28 4
gpt4 key购买 nike

这是说明问题的最小代码:

StringBuilder input = new StringBuilder();

void ToUpper()
{
lock (input)
{
while (true)
{
Monitor.Wait(input);

Console.WriteLine(input.ToString().ToUpper());
}
}
}

public void Run()
{
new Thread(ToUpper) { IsBackground = true }.Start();

// "Avoid" the initial race
Thread.Sleep(100);

while (true)
{
lock (input)
{
input.Clear();
input.Append(Console.ReadLine());
Monitor.Pulse(input);
}
// Thread.Sleep(1);
}
}

忽略众所周知的初始竞争条件,我对 Pulse and Wait 的行为感到惊讶。

这是我所期望的:

  • “ToUpper”线程调用Wait => 它被插入监视器的等待队列
  • 主线程调用Pulse => 将“ToUpper”线程从等待队列“移动”到就绪队列,立即获取锁
  • 主线程在退出lock语句作用域时退出监听
  • “ToUpper”线程获取锁并处理输入,同时主线程再次注册锁的所有权

但是有两次“ToUpper”线程不处理输入,而是主线程立即执行其处理

以下是我的假设:

  • Pulse 不会立即“移动”“ToUpper”线程,因此就绪队列保持为空
  • 主线程释放锁,循环,再次请求锁
  • 因为就绪队列中没有其他人,所以它获得了所有权
  • 有时会执行提升“ToUpper”线程的请求,最终将其插入就绪队列
  • 主线程第二次Pulse并释放锁
  • 它循环,尝试重新获取锁,但“ToUpper”线程已经存在
  • 这次“ToUpper”线程获取锁并处理输入
  • 完成后再次休眠等待下一个信号
  • 主线程获取锁

为了检查这个假设,我强制主线程不那么急切,让另一个线程工作,通过添加 Thread.Sleep(1) => 在这种情况下,所有都是“按预期工作”。

所以这一切都归结为 Pulse 的行为可能不会立即将线程从等待队列推送到就绪队列。

这个问题真的来自这个可能的种族还是我遗漏了另一个微妙之处?

最佳答案

您关于添加 Thread.Sleep(1) 的评论确实是答案。 Pulse方法不保证等待对象的任何东西 - 它只是被释放到 ready queue它作为普通线程从中进行。没有任何关于调用 Pulse 的痕迹。因此,在调用 Pulse 之后,应用程序的行为与普通的双线程应用程序一样,并且就绪队列中有两个线程 - ToUpper 线程 Run 线程。因此,如果没有 Thread.Sleep(1)Run 线程可能会首先获得锁(如果没有它,我猜也有可能,只是可能性较小)。

我提供的第二个链接中的另一个重要评论:

An important feature of Monitor.Pulse is that it executes asynchronously, meaning that it doesn't itself block or pause in any way.

对于这种情况,AutoResetEvent类似乎更合适。此外,在 second link您可以找到使用 WaitPulse 的生产者-消费者场景示例。

关于c# - Monitor.Pulse 和 Monitor.Wait 之间的竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19752730/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com