这段代码大部分时间都有效,所以我想到了一些竞争条件。 结果类是不可变的,但我认为问题不在于该类。
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应该是最接近的答案(如果不是正确答案),即使问题不完整。
答案 0 :(得分:2)
在您给出的代码中似乎没有任何特别的问题,这表明代码中可能存在未显示的导致问题的内容。我假设您使用的worker
是您的代码库的一部分(而不是像BackgroundWorker
那样的.NET BCL的一部分?)可能值得为此发布代码,以防万一是一个导致问题的问题。
例如,如果从多个线程重复使用同一个worker(或者有一个bug,Completed
可以为同一件工作多次引发,那么如果工人使用“通常“用于调用事件处理程序的方法,即:
var handler = Completed;
if (handler != null)
{
handler(...);
}
在<{em> var handler = Completed;
子句之前,您可以拥有一个finally
执行的实例(在completedHandler
与Completed
分离之前也是如此}事件),但在退出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;
}
}