C# 被动扩展会吞下在线程池线程上调用的OnNext()的异常吗?

C# 被动扩展会吞下在线程池线程上调用的OnNext()的异常吗?,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,我在.NET4.5中使用RX2。当下面的代码运行时,它只是静静地退出,而不执行OnCompleted委托或显示任何错误。如果我在ToObservable中使用Scheduler.CurrentThread,它至少会抛出错误并终止程序,此时不执行OnCompleted是有意义的。但是当这是在主线程以外的线程中执行时,这种行为似乎是不合理和不可接受的。我错过什么了吗 static void Main() { Enumerable.Range(0, 1)

我在.NET4.5中使用RX2。当下面的代码运行时,它只是静静地退出,而不执行OnCompleted委托或显示任何错误。如果我在
ToObservable
中使用
Scheduler.CurrentThread
,它至少会抛出错误并终止程序,此时不执行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秒(线程睡眠时间)后静默退出,没有任何错误或消息(预期为“已完成”)。那么,是它导致了问题吗

[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);
    }
}

订阅块中抛出的异常具有未定义的行为。如果您正在做一些可以抛出的操作,则需要将其包装在
Select
SelectMany
中(或者只将代码包装在try-catch中)。

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

注意:不保护对Subscribe、Dispose、OnNext、OnError和OnCompleted方法的调用。这些调用位于单子的边缘。从这些位置调用OnError方法将导致意外行为

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

但更重要的是,它还确保OnNext处理程序引发的任何异常都不会得到处理。这使得调试程序更容易,并保护用户数据

也就是说,在池线程上执行OnNext时,您可能会观察到不同行为的原因仅仅是调试经验的结果。尝试启用


此外,我还可以通过将
Thread.Sleep
更改为
Console.ReadKey()

来避免争用情况。异常不是由Subscribe引发的,而是由OnNext引发的。Rx设计指南明确了这一行为:不处理异常。无论如何,我不确定Select会有什么帮助。它只会将异常推送到OnError中,而OnError没有为其提供处理程序,因此无论如何都会再次抛出异常。(这也违背了指导方针的建议。)@DaveSexton你说得对,它仍然会崩溃,我给出了更一般的建议,当你想运行可能会抛出的代码时该怎么做。当然,这很好,但我也特别不同意你的建议。这纯粹是建设性的批评。我在回答中解释说,Rx中的建议不是捕获观察者抛出的异常并将它们重定向到OneError。如果OP的观察员可能会抛出,然后,他们不应更改为Select运算符,以允许Rx捕获异常,因为1)它使异常变得不太有用2)它使OnError变得不太有用,并使其本身易于出错3)仅为此目的使用投影运算符在语义上是不正确的。还可以尝试在未连接调试程序的情况下运行,您将看看你期待的行为。谢谢,@DaveSexton。是的,作为控制台应用程序运行是可以的;实际上,在NUnit测试中没有的是。请看我编辑的问题。这是一个比赛条件。我已经说过,您应该避免使用Thread.Sleep.com,更清楚地说,您现在问的是一个完全不同的问题:“我如何在NUnit中运行并发测试?”此外,您不应该期望调用OnCompleted。正如我所说的,Rx不会捕获观测者抛出的异常。因此,可观察到的立即停止。没有更多的通知。