Reactive Extensions吞下在线程池线程上调用的OnNext()的异常?

时间:2014-09-13 17:51:01

标签: c# system.reactive reactive-programming

我在.Net 4.5中使用Rx 2。当以下代码运行时,它只是静默退出而不执行OnCompleted委托或显示任何错误。如果我在Scheduler.CurrentThread中使用ToObservable,它将至少抛出错误并终止程序,此时不执行OnCompleted是有意义的。但是当它在主线程之外的线程中执行时,这种行为似乎是不合理的,也是不可接受的。我错过了什么吗?

static void Main()
{
     Enumerable.Range(0, 1)
                           .ToObservable(Scheduler.Default)
                           .Subscribe(o => { throw new Exception("blah"); }, () => Console.WriteLine("completed"));

     Thread.Sleep(2000);
 }

编辑: 是的,当作为控制台应用程序运行时,无论执行观察的是什么线程,它都将始终抛出错误。

但是,当我在NUnit中将此代码作为测试运行时,如下所示,它在2秒后(线程休眠时间)静默退出,没有任何错误或消息(期望"已完成")。那么实际上NUnit会导致这个问题吗?

[TestFixture]
class Program
{
    [Test]
    public void Test()
    {
        Enumerable.Range(0, 1)
                .ToObservable(Scheduler.Default)
                .Subscribe(
                    o => { throw new Exception("blah"); }, 
                    () => Console.WriteLine("completed"));
        Thread.Sleep(2000);
    }
}

2 个答案:

答案 0 :(得分:8)

Rx不会捕获观察者抛出的异常。这是一个非常重要的设计原则,之前已经详细讨论过,但由于某种原因,它仅作为Rx Design Guidelines中§6.4的脚注。

  

注意:不保护对订阅 Dispose OnNext OnError <的调用和 OnCompleted 方法。这些电话是在monad的边缘。从这些地方调用 OnError 方法会导致意外行为。

本质上,本指南确保从观察者的角度来看, OnError 只会由来自observable本身的异常调用,包括对直接参与计算的用户代码的任何调用(而不仅仅是观察结果)。如果不是这种情况,那么观察者可能无法区分传递给 OnError 的异常是否是 OnNext 处理程序中的错误,或者可能是可观察到的错误

但更重要的是,它还确保 OnNext 处理程序抛出的任何异常都不会被处理。这样可以更轻松地调试程序并保护用户数据。

话虽这么说,当您在池化线程上执行 OnNext 时,您可能正在观察不同行为的原因仅仅是您的调试经验的结果。尝试启用first-chance exceptions

此外,我还可以通过将Thread.Sleep更改为Console.ReadKey()来避免竞争条件。

答案 1 :(得分:3)

订阅块中抛出的异常具有未定义的行为。如果你正在做一些可以抛出的东西,你需要将它包装在SelectSelectMany中(或者只是将代码包装在try-catch中)。