Monitor vs WaitHandle基于线程同步

Mat*_*att 32 c# multithreading

在阅读本文后,我认为最好使用Monitor/Lock进行线程同步,因为它不使用本机资源

具体报价(来自文章第5页):

Monitor.Wait/Pulse不是在一个线程中等待某事发生并且告诉该线程它在另一个线程中发生的唯一方法.Win32程序员长期以来一直在使用各种其他机制,这些机制由AutoResetEvent,ManualResetEvent和Mutex类公开,所有这些都派生自WaitHandle.所有这些类都在System.Threading命名空间中.(Win32 Semaphore机制在.NET 1.1中没有托管包装.它存在于.NET 2.0中,但是如果你需要在那之前使用它,你可以使用P/Invoke自己包装它,或者编写你自己的计数信号量类.)

有些人可能会惊讶地发现使用这些类可能比使用各种Monitor方法慢得多.我相信这是因为将托管代码"输出"到本机Win32调用并再次返回"in"与Monitor提供的完全托管视图相比是昂贵的.读者还解释了监视器是在用户模式下实现的,而使用等待句柄需要切换到内核模式,这是相当昂贵的.

但是,自从发现SO并阅读一些问题/答案后,我开始怀疑我对何时使用每个问题的理解.似乎许多人建议在Monitor.Wait/Pulse可以使用的情况下使用Auto/ManualResetEvent.任何人都可以向我解释基于WaitHandle的同步应该在Monitor上使用吗?

谢谢

dtb*_*dtb 56

问题Monitor.Pulse/Wait是信号可能会丢失.

例如:

var signal = new ManualResetEvent(false);

// Thread 1
signal.WaitOne();

// Thread 2
signal.Set();
Run Code Online (Sandbox Code Playgroud)

无论在哪个执行不同线程中的两个语句,这都将始终有效.它也是一个非常干净的抽象,非常清楚地表达了你的意图.

现在看一下使用监视器的相同示例:

var signal = new object();

// Thread 1
lock (signal)
{
    Monitor.Wait(signal);
}

// Thread 2
lock (signal)
{
    Monitor.Pulse(signal);
}
Run Code Online (Sandbox Code Playgroud)

Pulse如果Pulse之前执行,signal()将丢失Wait.

要解决此问题,您需要以下内容:

var signal = new object();
var signalSet = false;

// Thread 1
lock (signal)
{
    while (!signalSet)
    {
        Monitor.Wait(signal);
    }
}

// Thread 2
lock (signal)
{
    signalSet = true;
    Monitor.Pulse(signal);
}
Run Code Online (Sandbox Code Playgroud)

这可以工作,可能更高性能和轻量级,但可读性较差.这就是头脑所谓的并发性开始的地方.

  • 这段代码真的有效吗?
  • 在每个角落的情况下?
  • 有两个以上的线程?(提示:它没有)
  • 你如何进行单元测试呢?

可靠,可靠,可读的抽象通常比原始性能更好.

另外,WaitHandles提供了一些很好的东西,比如等待设置一组句柄等.用监视器实现这一点会使头痛更加严重......


经验法则:

  • 使用Monitors(lock)确保对共享资源的独占访问
  • 使用WaitHandles(Manual/AutoResetEvent/Semaphore)在线程之间发送信号