Monitor.Wait,Pulse - 当工作线程有条件地表现为实际工作线程时

时间:2010-05-12 11:37:35

标签: .net multithreading

我的特殊情况: - 主线程启动工作线程。 - 主线程需要阻塞自己,直到工作线程完成(是的好笑)或工作线程本身通知主线程继续

好吧,我在主线程中做了什么:

wokerThread.Start(lockObj);
lock(lockObj)
 Monitor.Wait(lockObj);

工作线程中的某个地方:

if(mainThreadShouldGoOn)
 lock(lockObj)
  Monitor.Pulse(lockObj);

此外,在工作线程结束时:

lock(lockObj)
 Monitor.Pulse(lockObj);

到目前为止,它的工作非常完美。但这是一个很好的解决方案吗?还有更好的吗?

编辑:

如果我在主线程中这样做怎么办?

Monitor.Enter(lockObj);
workerThread.Start(lockObj);
Monitor.Wait(lockObj);

工人看起来像这样:

void Worker(object lockObj)
{
 Monitor.Enter(lockObj);
 ...
 ...
 ...
 if(mainThreadShouldGoOn)
 {
  Monitor.Pulse(lockObj);
  Monitor.Exit(lockObj);
 }
 ...
 ...
 ...
 if(!mainThreadShouldGoOn)
 {
  Monitor.Pulse(lockObj);
  Monitor.Exit(lockObj);
 }
}

2 个答案:

答案 0 :(得分:1)

此代码不正确,如果工作人员太快完成,您将面临永久阻止主线程的风险。这里使用的正确同步对象是ManualResetEvent(或auto,无关紧要)。在主线程中调用其Wait()方法,在worker中调用其Set()方法。只要它是一个Thread而不是一个线程池线程,使用Thread.Join()就可以了。

答案 1 :(得分:0)

为什么你刚刚开始工作线程才能入睡?你可以简单地在主线程中完成工作!

但假设你有理由这样做......

我认为您的解决方案在概念上可以,但可能存在问题。考虑在主线程启动工作线程之后但在获取锁定之前发生上下文切换的情况。然后工人将在一个时间片内快速完成其工作。在这种情况下,在Pulse之前调用Wait,因此稍后主线程将永远不会从等待中唤醒。这是一个例子:

class Program
{
    static object theLock = new object();

    static void Main(string[] args)
    {
        Thread worker = new Thread(WorkerMain);
        worker.Start();
        Thread.Sleep(1);
        lock (theLock)
        {
            Monitor.Wait(theLock);
        }
        Console.WriteLine("Main done");
        Console.ReadLine();
    }

    static void WorkerMain()
    {
        lock (theLock)
        {
            Monitor.Pulse(theLock);
        }
    }
}

大多数情况下此代码都会挂起。您可以通过将worker.Start移动到锁定范围内来解决问题。

还要注意的另一件事是确保调用Pulse,即使在特殊情况下也是如此。使用try finally是正确的,你会没事的。

或者考虑将工作人员的任务分为两部分:一部分总是要执行,另一部分现在跟在if(mainThreadShouldGoOn) {...}检查之后。然后你可以在main启动的工作线程中执行第一部分,如果需要,可以从第一个工作程序启动另一个工作线程的第二部分。然后,您可以使用Thread.Join()等待第一个工作人员完成。这样可以通过牺牲一点性能来简化同步,减少错误。