You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现更安全的Rx Finally变体运算符FinallySafe?

Rx 内置Finally运算符行为异常及自定义实现需求

最近我发现Rx的Finally运算符的行为不符合预期。我原本以为finallyAction抛出的任何错误都会向下游传递给该运算符的观察者,但实际并非如此。实际情况是该运算符先将上游序列的完成或失败通知传递给观察者,再执行action,此时已经无法传递action抛出的潜在错误,因此错误会被抛到ThreadPool上导致进程崩溃,这一问题不仅不符合预期,还会引发严重故障。

以下是该行为的最小复现示例:

Observable
    .Timer(TimeSpan.FromMilliseconds(100))
    .Finally(() => throw new ApplicationException("Oops!"))
    .Subscribe(_ => { }, ex => Console.WriteLine(ex.Message),
        () => Console.WriteLine("Completed"));

Thread.Sleep(1000);

运行结果:未处理异常
Finally lambda抛出的异常不会被Subscribe的onError处理程序捕获,而这是我们期望达到的效果。

在我看来,这个特性(我更倾向于称之为缺陷)严重限制了Finally运算符的实用性。本质上我只能用它执行那些预期永远不会失败的操作,这类操作如果失败就代表应用状态发生了灾难性损坏,没有恢复可能。比如我可以用它来Release一个SemaphoreSlim,这类操作只有代码存在bug时才会失败,这种情况下应用崩溃我是可以接受的。但我最近在实现时需要调用调用方提供的未知操作,这类操作有可能失败,这种情况下应用崩溃是不可接受的,错误应该向下游传递。因此我想请教如何实现一个Finally的变体,我们暂且称之为FinallySafe,它和原运算符签名一致,行为规范如下:

public static IObservable<TSource> FinallySafe<TSource>(
    this IObservable<TSource> source, Action finallyAction);
  • finallyAction需要在source序列发出OnCompleted或OnError通知之后,但在该通知传递给观察者之前执行。
  • 如果finallyAction执行成功,就将原始的OnCompleted/OnError通知传递给观察者。
  • 如果finallyAction执行失败,就将包含执行错误的OnError通知传递给观察者,这种情况下导致source失败的原有错误需要被忽略,不向下传递。
  • 如果在source完成之前就取消订阅FinallySafe,也需要执行finallyAction。当订阅者(观察者)释放订阅时,需要同步执行finallyAction,所有错误都需要传递给Dispose方法的调用方。
  • 如果多个观察者订阅了FinallySafe,需要为每个订阅独立调用一次finallyAction,遵循上述规则,允许并发调用。
  • 每个订阅的finallyAction最多只能被调用一次。

验证要求:将上述示例代码中的Finally替换为FinallySafe后,程序不会因为未处理异常而崩溃。

备选回答:如果能够合理解释内置Finally运算符的行为为什么优于上述自定义FinallySafe运算符的行为,也可以作为有效答案。


内容的提问来源于stack exchange,提问作者Theodor Zoulias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 17:06:00