多线程、竞争条件和c#事件——为什么不试试/catch呢?
本文关键字:为什么不 catch 事件 竞争 条件 多线程 | 更新日期: 2023-09-27 17:50:59
我在看2009年的这些问题:c#事件与线程安全
我还看了Eric Lippert关于同一主题的博客文章:http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
这个问题似乎归结为,在多线程环境中,你偶尔会遇到不同的线程在你的线程(线程a)之间取消注册委托的情况。'if'语句和'call delegate'语句。
当我阅读时,这个问题出现在我的脑海中:如果替代方案似乎是在竞争条件或抛出异常之间的选择,那么为什么不把它包装在一个try/catch块中,而不担心这两个?如果捕获了nullReferenceException,只需在catch块中忽略它(只是为了抑制异常),然后继续。
现在,我知道Eric Lippert和John Skeet对c#,多线程和委托有一点了解,所以有人可以花点时间解释一下我在这里错过了什么吗?这种异常吞入的主要问题是您不能确定NullReferenceException
是由事件委托是null
引起的。它可能是从委托本身内部抛出的。
只是为了确定-你说的是,而不是这样做:
Action temp = Foo;
if (temp != null)
temp();
要做到这一点:
try {
Foo();
}
catch (NullReferenceException e)
{
// magically ignore it
}
因此,Foo()
调用可能由于多种原因导致类似的异常,并且您将屏蔽不相关的异常。
一般来说,当抛出异常时,意味着发生了错误。
捕获异常并不能使错误消失。你的手术还是失败了。它所做的只是给你一个机会去回应正在发生的错误。
我认为这不是这篇文章的重点。如果替代选项似乎是在竞争条件或抛出异常之间的选择
正如Eric指出的,有两个问题:
- 委托可以为null
- 处理程序本身可能容易受到竞争条件的影响;仅仅因为注销代码是类似
event -= handler; DisposeRequiredResources();
的东西并不意味着处理程序代码在调用dispose之后将永远不会运行。
问题1通常通过临时变量来解决。(这是必需的,if (Foo != null) Foo();
由于竞争条件而无法工作)。其他解决方法包括使用空委托进行初始化。在这里使用try/catch不仅需要更多的输入,而且还有在事件处理程序中吞下真正的NullReferenceExceptions的危险。
问题2通过使处理程序健壮来解决。将整个调用放在try/catch块中并不能缓解这个问题。
也许我理解错了,但是你不能仅仅用try-catch来捕捉每一个比赛条件。参见下面的代码
int count = 0;
for (int i = 0; i < 1000; i++)
Task.Factory.StartNew(()=>count++,TaskCreationOptions.LongRunning);
Console.WriteLine(count);
这个可能会在多次运行时打印出1000以外的内容。