繁体   English   中英

使用ManualResetEvent可能出现的竞赛情况

[英]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()

如果您的线程不返回值,而您只想启动它们,然后让启动线程等待它们退出,那么我认为Parallel.Invoke()将是最好的方法。 它避免了您必须自己处理同步。

(否则,如svick在上面的评论中所述,请使用Task而不是旧的线程类。)

我没有直接回答这个问题。 这是您应该做的:

使用Task.Factory.StartNew启动任务,然后使用Task.WaitAll(Task[])等待Task.WaitAll(Task[]) 您不必以这种方式处理事件。 异常会很好地传播到“ forking”线程。 您不再需要旧的ThreadPool API。

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM