0

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

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()之前关闭,尽管 Completed 事件触发了两次。这根本没有影响 IO 工作(结果是正确的;在我更改代码以手动关闭waitHandle 之后)。

因此,即使问题不完整,铱星的答案也应该是最接近的答案(如果不是正确的答案)。

4

3 回答 3

2

在您给出的代码中似乎没有什么特别的问题,这表明您未显示的代码中可能存在导致问题的东西。我假设worker您正在使用的是您的代码库的一部分(而不是 .NET BCL 的一部分BackgroundWorker?)它可能值得发布代码,以防出现导致问题的问题。

例如,如果同一个工作线程被多个线程重复使用(或者有一个错误Completed可以为同一个工作多次引发),那么如果工作线程使用“通常”的方式来调用事件处理程序, IE:

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

您可以有一个实例 where在子句之前var handler = Completed;执行(因此在从事件中分离之前),但在退出块之后调用(因此在处理之后)。然后,您的事件处理程序将在处置后执行,并且您看到的异常将被抛出。finallycompletedHandlerCompletedhandler(...)using(...)ManualResetEventwaitHandle

于 2012-12-31T16:19:26.720 回答
1

没有明显的原因导致发布的代码会失败。但是我们看不到堆栈跟踪,也看不到触发 Completed 事件的逻辑,因此几乎没有机会为您调试它。任意地,如果事件多次触发,那么您肯定会遇到这种种族问题。

棘手的线程问题很难调试,线程竞争是发生在微秒级的问题。尝试调试它就足以让比赛消失。或者它发生得如此罕见,以至于有任何发现问题的希望都太罕见了,以至于无法证明尝试的合理性。

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

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

于 2012-12-31T16:13:16.900 回答
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;
        }
}
于 2012-12-31T15:40:51.603 回答