【问题标题】:How can I stop IConnectableObservable.Wait() from swallowing unhandled exceptions?如何阻止 IConnectableObservable.Wait() 吞下未处理的异常?
【发布时间】:2020-04-18 22:12:56
【问题描述】:

我有一个使用 Rx.NET 的控制台应用程序。

我需要阻塞直到 IConnectableObservable 完成,为此我使用IConnectableObservable.Wait()

当一个未处理的异常被抛出时,它被吞下并且应用程序挂起。我希望应用程序崩溃,并将堆栈跟踪打印到控制台。

我不想将OnError 处理程序添加到我的IConnectableObserver,因为这会丢失原始堆栈跟踪。

我曾尝试在未发布的 observable 上使用 .Wait() 方法,但这会重新订阅会导致不良行为。

我尝试过使用.GetAwaiter().GetResult(),但这有同样的问题。

var connectable = myObservable.Publish();

connectable.Subscribe(myObserver1);
connectable.Subscribe(myObserver2);

connectable.Connect();
connectcable.Wait();

如何在保留典型的未处理异常行为的同时等待 IConnectableObservable 完成?

【问题讨论】:

  • 我重现了这个问题。对我来说它看起来像一个错误。是否可以选择Wait 源可观察对象而不是可连接对象?
  • 很高兴知道这不仅仅是我。 Waiting 源 observable 是一个选项,但如上所述在源 observable 上调用 .Wait() 会再次订阅 observable,从而开始不同的结果序列。
  • 是的,你是对的。奇怪的是,添加 onError 处理程序如何改变可连接的等待者的行为!
  • 你试过connectable.Subscribe(myObserver1);而不是connectable.Subscribe(Console.WriteLine, exn => Console.WriteLine("Handled"));吗?默认的 OnError 方法是 throw - 可以在 Appdomain 计时器之类的上下文中。
  • 我实际上写了答案,但我会在发布之前让你确认。 Wait 最终成为了一条红鲱鱼。 +1 混乱。

标签: c# exception .net-core system.reactive


【解决方案1】:

这里的事件链中有一些误导。 错误并没有被吞没 - 远非如此,它正在被重新抛出。

通常怀疑是一些奇怪的并发和调度问题,但没有人怀疑Subscribe 方法。

当您使用自己的IObserver<T> 以外的其他内容调用Subscribe 时,您正在使用这些默认操作创建AnonymousObserver

new AnonymousObserver<T>(Ignore, Throw, Nop)

这是有效的

new AnonymousObserver<T>(_ => {}, exn => throw exn, () => {})

默认错误处理程序将在您观察的任何上下文中抛出错误。哎呀。有时它可能是 AppDomain 计时器,或者在一个池线程上,由于无法处理,您的应用程序出现故障。

因此,如果我们将示例更改为在虚拟处理程序中提供,

var myObservable = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(4).Concat(Observable.Throw(new Exception(), 1L));
var connectable = myObservable.Publish();

connectable.Subscribe(Console.WriteLine, exn => Console.WriteLine("Handled"));
connectable.Subscribe(Console.WriteLine, exn => Console.WriteLine("Handled"));

connectable.Connect();

try
{
    connectable.Wait();
}
catch (Exception)
{
    Console.WriteLine("An error, but I'm safe");
}

您可以按预期处理Wait 中的错误。

【讨论】:

  • 非常有趣! Lee Campbell 的电子书(Lifetime management - Subscribing 章)中实际上提到了它,但我完全忘记了它:“需要注意的关键点是,如果您使用的重载没有为 OnError 指定委托通知,任何 OnError 通知都将作为异常重新抛出。” 这应该是大写字母和粗体! ?
  • ALLCAPS 和 32 pt 字体,哈哈。在此示例中,重新抛出实际上发生在计时器线程上并清除了应用程序。考虑到有多少人在任何地方都随意使用Subscribe,这很可怕。
  • 另外,我浪费了一些时间,因为这个标签上的问题已经训练我先去跟踪调度程序。 :D
  • 确实很可怕。我在这个网站上看到很多Subscribes 缺少OnError 代表!如果您经常使用Do 运算符进行调试,也很容易忘记这个问题,因为省略OnError 操作不会产生不良后果。
  • 您提到如果在错误的上下文中引发错误,应用程序就会崩溃。在我最初的问题的情况下,应用程序只是无限期挂起(让它 +5m 进行测试),任何地方都没有通知。为什么这可能表现不同?主要是出于好奇,因为像您建议的那样添加一个虚拟处理程序可以解决问题,这很好。
猜你喜欢
  • 2012-03-06
  • 1970-01-01
  • 2014-08-18
  • 1970-01-01
  • 2020-04-20
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多