[英]Possible Race condition with ManualResetEvent
问题:
我试图从ThreadPool抛出6个线程来处理单个任务。 每个任务的ManualResetEvent存储在一组手动重置事件中。 线程数与ManualResetEvent数组中的索引相对应。
现在发生的事情是,一旦启动了这6个线程,我就会移出并等待线程完成。 等待线程在主线程中完成。
现在有时候会发生的事情是,即使经过很长一段时间(我看到的两天),我的等待逻辑也没有返回。 这是线程等待逻辑的代码示例
foreach (ManualResetEvent whandle in eventList)
{
try
{
whandle.WaitOne();
}
catch (Exception) { }
}
根据.WaitOne的文档。 这是同步调用,如果未从线程接收到Set事件,则使线程不返回。
有时,我的线程工作量较少,甚至可能在我到达“等待”逻辑之前返回。 即使过去收到过.WaitOne()是否也可能会等待Set()事件? 这是等待所有线程关闭的正确逻辑吗?
(注意:我认为最好的选择是Parallel.Invoke()
-参见此答案的后面。)
您正在执行的操作通常都可以正常工作,因此问题可能出在您的线程之一由于某种原因被阻塞了。
您应该能够足够容易地调试它-您可以连接调试器并进入程序,然后查看调用堆栈以查看哪些线程被阻塞。 不过,如果您发现比赛状况,请做好准备以防万一!
请注意,您不能执行以下操作:
myEvent.Set();
myEvent.Reset();
.Set()
和.Reset()
之间没有任何内容(或很少.Reset()
。 如果在myEvent
等待多个线程时执行此操作,则其中一些线程将错过正在设置的事件! (这种效果在MSDN上没有得到很好的记录。)
顺便说一句,您不应忽略异常-至少始终以某种方式记录异常。
(本节未回答问题,但可能会提供一些有用的信息)
我还想提到一种等待线程的替代方法。 由于您具有一组ManualResetEvents,因此可以将它们复制到纯数组并将其传递给WaitHandle.WaitAll()
。
您的代码可能看起来像这样:
WaitHandle.WaitAll(eventList.ToArray());
等待所有线程完成的另一种方法是使用CountdownEvent
。 当倒数到零时,它会发出信号。 您从线程数开始计数,并且每个线程在退出时都会发出信号。 这里有一个例子在这里 。
如果您的线程不返回值,而您只想启动它们,然后让启动线程等待它们退出,那么我认为Parallel.Invoke()
将是最好的方法。 它避免了您必须自己处理同步。
(否则,如svick在上面的评论中所述,请使用Task
而不是旧的线程类。)
我没有直接回答这个问题。 这是您应该做的:
使用Task.Factory.StartNew
启动任务,然后使用Task.WaitAll(Task[])
等待Task.WaitAll(Task[])
。 您不必以这种方式处理事件。 异常会很好地传播到“ forking”线程。 您不再需要旧的ThreadPool
API。
希望这可以帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.