.NET 响应式编程 System.Reactive 系列文章(三):Subscribe 和 IDisposable 的深入理解

.NET 响应式编程 System.Reactive 系列文章(三):Subscribe 和 IDisposable 的深入理解


引言:为什么理解 Subscribe 和 IDisposable 很重要?

在前两篇文章中,我们详细介绍了 IObservable<T>IObserver<T> 的核心概念及交互流程。但在实际使用 System.Reactive 时,一个常见的误区是认为数据流一旦订阅,就不需要额外管理。这种认知是危险的,因为 Observable 的订阅可能是无限的,如果不管理好订阅的生命周期,很容易导致内存泄漏资源浪费

在 Rx 中,Subscribe() 方法返回一个 IDisposable 接口对象,用于手动取消订阅和释放资源。另外,System.Reactive 还提供了不返回 IDisposableSubscribe 重载,这些重载方法通过 CancellationToken 管理订阅的生命周期。在本篇文章中,我们将深入探讨 Subscribe 和 IDisposable 的原理、这些特殊重载的设计原因,以及在实际使用中的应用场景。


1. Subscribe 的内部机制

1.1 Subscribe 的作用

Subscribe 是连接 IObservable<T>IObserver<T> 的桥梁。当你调用 Subscribe() 方法时:

  • IObservable<T> 开始向 IObserver<T> 推送数据
  • 订阅会保持活跃状态,直到:
    • 数据流结束(调用 OnCompleted())。
    • 发生错误(调用 OnError())。
    • 手动取消订阅(调用 Dispose())。
    • 超时取消订阅(向CancellationToken注册超时回调)。

1.2 为什么 Subscribe 返回 IDisposable?

普通的 Subscribe 重载 返回一个 IDisposable 对象,允许你通过调用 Dispose() 方法取消订阅。这是管理数据流生命周期的核心机制之一。


2. Subscribe 重载:不返回 IDisposable 的特殊情况

System.Reactive 提供了一些特殊的 Subscribe 重载方法,它们不返回 IDisposable,而是依赖于 CancellationToken 来控制订阅的生命周期。这些方法设计的目的是为了提供一种外部取消订阅的机制,让你无需手动管理 Dispose() 的调用。

2.1 方法签名

以下是其中一个不返回 IDisposableSubscribe 重载:

public static void Subscribe<T>(this IObservable<T> source,Action<T> onNext,Action<Exception> onError,Action onCompleted,CancellationToken cancellationToken
);

这种重载方法的使用场景是:你希望通过 CancellationToken 来控制订阅的生命周期,而不是手动调用 Dispose()


2.2 示例代码:使用 CancellationToken 管理订阅

示例:超时取消订阅

using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;class Program
{static void Main(string[] args){IObservable<long> observable = Observable.Interval(TimeSpan.FromSeconds(1));CancellationTokenSource cts = new();// 使用 Subscribe 方法并传入 CancellationTokenobservable.Subscribe(onNext: static value => Console.WriteLine($"Received: {value}"),onError: static ex => Console.WriteLine($"Error: {ex.Message}"),onCompleted: static () => Console.WriteLine("Completed"),token: cts.Token);// 模拟运行 5 秒后取消订阅Console.WriteLine("Running for 5 seconds...");Thread.Sleep(5000);cts.Cancel();Console.WriteLine("Subscription cancelled.");}
}

输出结果:

Running for 5 seconds...
Received: 0
Received: 1
Received: 2
Received: 3
Subscription cancelled.

2.3 使用场景:什么时候使用 CancellationToken?

使用场景 推荐的 Subscribe 重载
需要手动取消订阅 返回 IDisposable 的重载
使用外部控制(如用户交互、超时)控制订阅 CancellationToken 的重载

典型场景:

  1. 异步任务取消
    在异步任务中使用 CancellationToken 取消订阅数据流,避免阻塞或内存泄漏。

  2. 超时控制
    使用 CancellationTokenSource.CancelAfter() 设置超时取消订阅。


2.4 示例:设置超时取消订阅

using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;class Program
{static void Main(string[] args){IObservable<long> observable = Observable.Interval(TimeSpan.FromSeconds(1));CancellationTokenSource cts = new();cts.CancelAfter(TimeSpan.FromSeconds(3)); // 设置 3 秒后自动取消订阅observable.Subscribe(onNext: static value => Console.WriteLine($"Received: {value}"),onError: static ex => Console.WriteLine($"Error: {ex.Message}"),onCompleted: static () => Console.WriteLine("Completed"),token: cts.Token);Console.WriteLine("Running...");Thread.Sleep(5000);Console.WriteLine("Program ended.");}
}

输出结果:

Running...
Received: 0
Received: 1
Received: 2
Program ended.

3. 使用场景总结

使用方式 特点 适用场景
Subscribe 返回 IDisposable 允许手动取消订阅 长时间订阅或频繁管理多个订阅
Subscribe 接受 CancellationToken 通过外部控制(如超时或用户交互)取消订阅 异步任务、超时控制、用户交互场景

4. 注意事项:CancellationToken 的局限性

虽然使用 CancellationToken 可以简化订阅管理,但也有一些需要注意的地方:

  1. 不支持手动取消
    如果你使用的是返回 IDisposableSubscribe 方法,你可以手动调用 Dispose() 取消订阅。但如果你使用带 CancellationToken 的重载,就无法通过 Dispose() 取消订阅。

  2. 更适合一次性订阅
    CancellationTokenSubscribe 重载更适合一次性订阅的场景。如果你需要频繁管理多个订阅,使用 CompositeDisposable 或手动管理 IDisposable 可能更合适。


5. 两种订阅方式的对比

特性 返回 IDisposableSubscribe CancellationTokenSubscribe
是否支持手动取消订阅 ✅ 支持 ❌ 不支持
是否支持外部控制订阅生命周期 ❌ 需要手动调用 Dispose() ✅ 通过 CancellationToken 控制
是否适合长期订阅 ✅ 适合 ❌ 更适合一次性订阅

6. Subscribe 和 IDisposable 的交互流程图

sequenceDiagramparticipant Observer as IObserver<T>participant Observable as IObservable<T>participant IDisposable as IDisposableObserver ->> Observable: Subscribe()Observable ->> Observer: OnNext(T value)Observable ->> Observer: OnNext(T value)Observer ->> IDisposable: Dispose()Observable -->> Observer: 停止推送数据

总结

在本篇文章中,我们详细探讨了 Subscribe 和 IDisposable 的内部机制,并重点介绍了 CancellationTokenSubscribe 重载

  1. Subscribe() 方法返回 IDisposable,用于管理订阅的生命周期。
  2. 不返回 IDisposableSubscribe 重载,通过 CancellationToken 控制订阅的终止。
  3. 使用场景不同IDisposable 更适合长期订阅,CancellationToken 更适合一次性或外部控制的订阅。

下一篇文章预告

《.NET 响应式编程 System.Reactive 系列文章(四):操作符基础》
下一篇文章将介绍 System.Reactive 的基础操作符,包括如何创建转换过滤数据流。我们将通过实战示例,帮助你快速掌握 Rx 的操作符使用方法。敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/866174.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【关节电机专栏】ESP32-S3控制大然电机

如何进行CAN初始化? 以下是ESP32-TWAI-CAN库的CAN总线初始化函数(begin()) bool begin(TwaiSpeed twaiSpeed = TWAI_SPEED_500KBPS, int8_t txPin = -1, int8_t rxPin = -1,uint16_t txQueue = 0xFFFF, uint16_t rxQueue = 0xFFFF,twai_filter_config_t* fConfig = nullptr,…

1.2.7 PMU设计中常见问题的分析解答

PMU设计中常见问题的分析解答 在 PMU(电源管理单元)设计中,常见的问题通常涉及电源性能、热管理、效率、安全性等多个方面。以下是一些常见问题及其分析与解答: 1. 输出电压不稳定 问题:PMU 的输出电压无法达到预期,存在较大的波动。 原因分析:负载变化:当负载发生变化…

1.2.6 PMU电源的散热处理及主要事项

PMU电源的散热处理及主要事项 PMU(Power Management Unit,电源管理单元)作为电子设备中实现电源调节和转换的重要组件,其散热处理至关重要,因为过高的温度会影响性能、可靠性和寿命。以下是 PMU 电源的散热处理方法及主要事项。 一、PMU 电源的散热处理方法选择合适的封装…

1.2 PMU

PMU PMU(Power Management Unit,电源管理单元)是一种集成电路(IC),用于管理和调节电子设备中的电源和电压供应。PMU 的设计旨在提高能源利用效率,减少能量损耗,并确保设备在各种操作条件下的稳定运行。以下是关于 PMU 的详细介绍,包括其功能、组成部分、工作原理和应用…

1. 电源模块

电源模块 电源模块是将电源转换、调节和管理功能集成在一个单元中的电子设备。它们通常用于提供电力和接口,确保电子设备在运行过程中获得稳定的电源供应。以下是关于电源模块的详细讲解,包括其类型、特性、应用等。 1. 什么是电源模块? 电源模块是集成多个组件(如变压器、…

第3章 命名准则

第3章 命名准则 1 大小写约定 1 标识符的大小写规则​DO​: 命名空间 、 类型 、 成员 和 泛型参数 ,使用 PascalCasing 风格命名。‍​DO​​:参数,使用 camelCasing 风格命名。‍标识符 大小写 示例命名空间 Pacal namespace System.Security类型 Pacal public class S…

0. PCB模块

PCB模块 PCB(印刷电路板)模块是在电子设计和应用中非常常见的组件。它们是将电路功能集成到一个统一单元中的关键部分。下面,对 PCB 模块进行详细讲解,包括其定义、类型、应用和设计考虑等方面。 1. 什么是 PCB 模块? PCB 模块是指在印刷电路板上实现特定功能的预制电路板…

MarkDown使用方法

Markdown学习 标题 三级标题 四级标题 字体 Hello,World! Hello,World! Hello,World! Hello,World! 引用好好学习,天天向上分割线图片超链接 [点击跳转到csdn](CSDN - 专业开发者社区) 列表A B CA B C表格名字 性别 生日张三 男 1997.1.1代码 public

1.5 电源模块散热的处理

掌握电源模块散热的处理 电源模块的散热设计对于确保电子设备的性能和可靠性至关重要。由于电源模块在转换电能的过程中会产生热量,如果不合理处理散热,可能会导致模块过热,从而影响其工作效率、使用寿命,甚至导致系统故障。以下是电源模块散热处理的一些关键原则和方法: …

1.4 覆铜宽度和电流大小之间的关系,掌握过孔大小与数量与电流大小的关系

覆铜宽度和电流大小之间的关系,掌握过孔大小与数量与电流大小的关系 在 PCB(印刷电路板)设计中,覆铜宽度和过孔的大小与电流处理能力之间的关系是非常重要的。这些因素直接影响到电路的安全性、性能和热管理能力。以下是关于覆铜宽度、电流大小、过孔大小与数量与电流之间关…

1.3 什么是反馈电路, 反馈的摆放方法及布线取样点的设置

什么是反馈电路, 反馈的摆放方法及布线取样点的设置 反馈电路的概念 反馈电路是一种通过输出信号的信息返回到输入端,以调节或控制电路性能的系统。在电子电路中,反馈可以用来增强系统的稳定性、改善线性度、提高增益、降低失真等。反馈电路可以分为两种主要类型:正反馈:输…

解决方案 | aardio 实现界面自动缩放,控件随之变换

只需要把所有控件设置为 自适应大小true,固定边距禁用全部即可。网上说的花里胡哨,没个准信