多线程、竞争条件和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#,多线程和委托有一点了解,所以有人可以花点时间解释一下我在这里错过了什么吗?

多线程、竞争条件和c#事件——为什么不试试/catch呢?

这种异常吞入的主要问题是您不能确定NullReferenceException是由事件委托是null引起的。它可能是从委托本身内部抛出的。

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

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

要做到这一点:

try {
    Foo();
}
catch (NullReferenceException e)
{
    // magically ignore it
}
因此,Foo()调用可能由于多种原因导致类似的异常,并且您将屏蔽不相关的异常。

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

捕获异常并不能使错误消失。你的手术还是失败了。它所做的只是给你一个机会去回应正在发生的错误。

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

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

正如Eric指出的,有两个问题:

  1. 委托可以为null
  2. 处理程序本身可能容易受到竞争条件的影响;仅仅因为注销代码是类似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以外的内容。