WaitHandle在不应该关闭时关闭

时间:2012-12-31 15:29:34

标签: c# multithreading waithandle

这段代码大部分时间都有效,所以我想到了一些竞争条件。 结果类是不可变的,但我认为问题不在于该类。

public Result GetResult()
{
    using (var waitHandle = new ManualResetEvent(false))
    {
        Result result = null;

        var completedHandler = new WorkCompletedEventHandler((o, e) =>
        {
            result = e.Result;

            // somehow waitHandle is closed, thus exception occurs here
            waitHandle.Set();
        });
        try
        {
            this.worker.Completed += completedHandler;

            // starts working on separate thread
            // when done, this.worker invokes its Completed event
            this.worker.RunWork();

            waitHandle.WaitOne();

            return new WorkResult(result);
        }
        finally
        {
            this.worker.Completed -= completedHandler;
        }
    }
}

编辑:道歉,我在调用GetResult()方法之前就错过了对this.worker.RunWork()的调用。这显然导致(有时)两次做同样的工作,虽然我不确定为什么waitHandle在 waitHandle.Set()之前关闭,尽管已完成事件两次触发。这根本没有影响IO工作(结果是正确的;在我更改代码以手动关闭 waitHandle 之后)。

因此,Iridium's answer应该是最接近的答案(如果不是正确答案),即使问题不完整。

3 个答案:

答案 0 :(得分:2)

在您给出的代码中似乎没有任何特别的问题,这表明代码中可能存在未显示的导致问题的内容。我假设您使用的worker是您的代码库的一部分(而不是像BackgroundWorker那样的.NET BCL的一部分?)可能值得为此发布代码,以防万一是一个导致问题的问题。

例如,如果从多个线程重复使用同一个worker(或者有一个bug,Completed可以为同一件工作多次引发,那么如果工人使用“通常“用于调用事件处理程序的方法,即:

var handler = Completed;
if (handler != null)
{
    handler(...);
}

在<{em> var handler = Completed;子句之前,您可以拥有一个finally执行的实例(在completedHandlerCompleted分离之前也是如此}事件),但在退出handler(...)块之后,using(...)被称为(在ManualResetEvent被释放后)。您的事件处理程序将在waitHandle被释放后执行,并且您将看到异常。

答案 1 :(得分:1)

没有明显的理由说明为什么会因发布的代码而失败。但是我们看不到堆栈跟踪,我们无法看到启动Completed事件的逻辑,因此很少有机会为您调试。任意地,如果事件不止一次发生,那么你肯定会遇到这种种族问题。

令人烦恼的线程问题很难调试,线程竞争是微秒级发生的问题。尝试调试它足以让比赛消失。或者它很少发生,以至于没有希望解决这个问题,这种情况太少了,无法证明这种尝试是正确的。

此类问题通常需要记录以诊断比赛。一定要选择一种轻量级的记录方法,登录本身可以改变时间,足以防止比赛的发生。

最后但同样重要的是:请注意在此处使用线程没有意义。通过直接调用由RunWork()启动的任何线程执行的代码,您可以得到 exact 相同的结果。减少开销和头痛。

答案 2 :(得分:0)

如果你摆脱了使用你的代码不会在你指定的行中引发异常...... 如果你真的需要,你必须找到一个合适的地方来处理它。

public Result GetResult()
{
    var waitHandle = new ManualResetEvent(false);

        Result result = null;

        var completedHandler = new WorkCompletedEventHandler((o, e) =>
        {
            result = e.Result;

            // somehow waitHandle is closed, thus exception occurs here
            waitHandle.Set();
            waitHandle.Dispose();
        });
        try
        {
            this.worker.Completed += completedHandler;

            // starts working on separate thread
            // when done, this.worker invokes its Completed event
            this.worker.RunWork();

            waitHandle.WaitOne();

            return new WorkResult(result);
        }
        finally
        {
            this.worker.Completed -= completedHandler;
        }
}