应优先使用system.threading.channels或iobservable/iobserver替代public event eventhandler,因其可避免内存泄漏、线程不安全及订阅管理困难;channels支持背压与取消,iobservable支持响应式组合与生命周期管理;务必调用trycomplete()或oncompleted()结束流。

直接用 System.Threading.Channels 或 IObservable<t>/IObserver<t></t></t>,别手写事件委托链——容易内存泄漏、线程不安全、订阅关系难管理。
为什么不用 public event EventHandler?
手写 public event EventHandler<myargs> OnDataReceived</myargs> 看似简单,但实际项目里常踩三个坑:
- 订阅者没调用
-=解订阅 →EventHandler持有对象引用 → GC 不回收 → 内存泄漏(尤其在 UI 控件或生命周期短的对象上) - 多线程触发事件时,
Invoke前没判空 →NullReferenceException(.NET 6+ 虽加了原子读,但旧版仍需手动锁) - 无法区分“广播”和“点对点”,也不能控制缓冲、背压、取消 —— 一发错数据,所有监听者全崩
用 Channel<t></t> 实现带背压的发布订阅
适合需要控制消费节奏、防止生产者压垮消费者(比如日志批量落盘、传感器数据流处理)的场景。它原生支持异步、取消、满载阻塞。
-
Channel.CreateBounded<string>(new BoundedChannelOptions(100) { FullMode = BoundedChannelFullMode.Wait })</string>:设上限 + 阻塞写入,比无界ConcurrentQueue更可控 - 每个消费者用独立
ChannelReader<t></t>调用ReadAsync(),互不影响;一个ChannelWriter<t></t>可被多个 reader 共享 - 别直接 await
Writer.WriteAsync()在 UI 线程——可能死锁;改用await Writer.WriteAsync(..., cancellationToken)并传入页面级CancellationToken
var channel = Channel.CreateBounded<int>(10);
_ = Task.Run(async () => {
for (int i = 0; i
<h3>用 <code>IObservable<t></t></code> 替代事件,解决生命周期问题</h3>
<p>当需要响应式组合(如“连续 3 次失败才告警”)、绑定到 MVVM 的 <code>INotifyPropertyChanged</code>、或跨线程自动调度时,<code>IObservable<t></t></code> 是更干净的选择。</p>
<ul>
<li>用 <code>Observable.FromEventPattern()</code> 包装传统事件,自动处理订阅/反订阅</li>
<li>
<code>Subscribe()</code> 返回 <code>IDisposable</code>,把它存进类字段,<code>Dispose()</code> 时统一释放,避免漏解</li>
<li>别在 <code>Subscribe()</code> 回调里直接更新 UI 控件 —— 默认在生产者线程执行;加 <code>.ObserveOn(SynchronizationContext.Current)</code> 切回 UI 线程</li>
<li>第三方库推荐 <code>System.Reactive</code>(NuGet 包名),不是 <code>System.Observable</code>(后者只有接口定义)</li>
</ul>
<h3>完整实例里最容易被删掉的一行</h3>
<p>几乎所有开源示例都漏写这句:<code>channel.Writer.TryComplete();</code> 或 <code>subject.OnCompleted();</code>。没有它,<code>await foreach</code> 永远不退出,<code>Subscribe()</code> 的 <code>onCompleted</code> 回调永远不会触发 —— 表现为程序“卡住”或资源不释放,但又不报错。</p>
<p>它不是可选的收尾动作,是协议的一部分:告诉所有消费者“不会再有新数据了”。漏了就等于没关门,别人不敢走。</p></int>










