我从2009年开始研究这些问题: C# Events and Thread Safety
我还看了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#,多线程和代表有一点了解,所以有人可以花点时间解释我在这里缺少的内容吗?
答案 0 :(得分:2)
此类吞咽异常的主要问题是您无法确定NullReferenceException
是否由事件委托null
引起。它本可以从代表本身抛出。
只是为了确定 - 你所说的是,而不是这样做:
Action temp = Foo;
if (temp != null)
temp();
要做到这一点:
try {
Foo();
}
catch (NullReferenceException e)
{
// magically ignore it
}
同样,Foo()
调用可能会因许多原因导致类似的异常,并且您将屏蔽不相关的异常。
答案 1 :(得分:2)
一般来说,当抛出异常时,就意味着发生了错误。
捕捉和异常不会使错误消失。您的操作仍然失败。它所做的只是让你有机会回应发生的错误。
答案 2 :(得分:2)
如果替补选项似乎是竞争条件或抛出异常之间的选择
我认为这不是文章的重点。
埃里克指出,有两个问题:
event -= handler; DisposeRequiredResources();
并不意味着在调用dispose之后处理程序代码将不会运行。问题1通常用临时变量解决。 (这是必需的,if (Foo != null) Foo();
由于竞争条件不起作用)。其他解决方法包括使用空委托进行初始化。在这里使用try / catch不仅更多的输入,它还有吞噬事件处理程序中真正的NullReferenceExceptions的危险。
通过使处理程序健壮来解决问题2。将整个调用放在try / catch块中并不能解决问题。
答案 3 :(得分:1)
也许我理解你错了,但你不能只用try-catch来捕捉每一种竞争条件。请参阅以下代码
int count = 0;
for (int i = 0; i < 1000; i++)
Task.Factory.StartNew(()=>count++,TaskCreationOptions.LongRunning);
Console.WriteLine(count);
这可能会在多次运行时打印其他1000个。