多线程,竞争条件和C#事件 - 为什么不尝试/捕获?

时间:2013-01-08 22:39:58

标签: c# multithreading events

我从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#,多线程和代表有一点了解,所以有人可以花点时间解释我在这里缺少的内容吗?

4 个答案:

答案 0 :(得分:2)

此类吞咽异常的主要问题是您无法确定NullReferenceException是否由事件委托null引起。它本可以从代表本身抛出。

只是为了确定 - 你所说的是,而不是这样做:

Action temp = Foo;
if (temp != null)
      temp();

要做到这一点:

try {
    Foo();
}
catch (NullReferenceException e)
{
    // magically ignore it
}

同样,Foo()调用可能会因许多原因导致类似的异常,并且您将屏蔽不相关的异常。

答案 1 :(得分:2)

一般来说,当抛出异常时,就意味着发生了错误。

捕捉和异常不会使错误消失。您的操作仍然失败。它所做的只是让你有机会回应发生的错误。

答案 2 :(得分:2)

  

如果替补选项似乎是竞争条件或抛出异常之间的选择

我认为这不是文章的重点。

埃里克指出,有两个问题:

  1. 委托可以为null
  2. 处理程序本身可能容易受到竞争条件的影响;仅仅因为取消注册的代码类似于event -= handler; DisposeRequiredResources();并不意味着在调用dispose之后处理程序代码将不会运行。
  3. 问题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个。