c# - 在可观察事件上使用带有 TimeSpan 的 Rx Window 操作可防止垃圾收集

标签 c# garbage-collection system.reactive

我试图确保使用 Rx 的类实例不会泄漏。我已将问题简化为最基本的问题,看起来 Window 函数是问题的关键。

考虑以下因素:

<!-- language: c# -->

using System;
using System.Reactive.Linq;

class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();
        Console.WriteLine("Press any key...");
        Console.ReadKey(true);

        foo = null;
        GC.Collect();

        Console.WriteLine("Press any key...");
        Console.ReadKey(true);
    }
}

public class Foo
{
    private event EventHandler MyEvent;

    public Foo()
    {
        var subs = Observable.FromEventPattern(
            e => MyEvent += e, e => MyEvent -= e);

        // (1) foo is never GC'd
        subs.Window(TimeSpan.FromSeconds(1)).Subscribe();

        // (2) foo is GC'd
        //subs.Window(TimeSpan.FromSeconds(1));

        // (3) foo is GC'd
        // subs.Window(1);
    }

    ~Foo()
    {
        Console.WriteLine("Bye!");     
    }
}

当我使用 TimeSpan 打开选择器应用 Window 函数并订阅它时,(1) foo 永远不会被 GC 回收。

如果我不订阅 (2),或者我使用不同的打开选择器 (3),那么它就是。

此外,如果我使用冷可观察量作为源,那么 foo 无论如何都会被 GC 处理。

为什么带有 TimeSpan 的 Window 函数很特殊,以及如何确保 foo 在使用它时会被 GC 回收?

最佳答案

这对我来说看起来是正确的。

您缺少对 IDisposable 字段的订阅分配。您不会处置订阅,也不会调用 GC.WaitForPendingFinalizers();

您可以使用以下方法修复测试:

public class Foo : IDisposable
{
    private event EventHandler MyEvent;
    private readonly IDisposable _subscription;

    public Foo()
    {
        var subs = Observable.FromEventPattern(
            e => MyEvent += e, 
            e => MyEvent -= e);

        // (1) foo is never GC'd
        //subs.Window(TimeSpan.FromSeconds(1)).Subscribe();
        _subscription = subs.Window(TimeSpan.FromSeconds(1)).Subscribe();

        // (2) foo is GC'd
        //subs.Window(TimeSpan.FromSeconds(1));

        // (3) foo is GC'd
        // subs.Window(1);
    }

    public void Dispose()
    {
        _subscription.Dispose(); 
    }

    //TODO: Implement Dispose pattern properly
    ~Foo()
    {
        _subscription.Dispose();
        Console.WriteLine("Bye!");     
    }
}

测试现在可以变成

//foo = null;   //This will just change our reference, the object sill lives and has stuff happening with timers etc..
foo.Dispose();  //Dispose instead of killing reference


GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

我希望这会有所帮助。另请查看Lifetime Management发表在我的 Rx 博客系列简介上。

更新:我的在线图书 IntroToRx.com取代博客系列。这里最相关的似乎是 Lifetime management章。

关于c# - 在可观察事件上使用带有 TimeSpan 的 Rx Window 操作可防止垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9260321/

相关文章:

c# - 发生压缩后GC如何更新引用

c# - 使用 C# : A better design to perform validation in Value Objects 清理架构

对 key 对进行排序并可访问的 C# 结构?

时间:2019-03-17 标签:c#EventlogFullorNot

c# - C# volatile 变量:内存隔离VS。快取

关于清除链表的 Java 最佳实践

java - 如果垃圾收集器在同一代码中两次保留内存,是否可以调用两次 finalize() 方法?

c# - 我应该在 Reactive Extensions (Rx) Subject<T> 上调用 Dispose

c# - 仅当值不在本地缓存中时调用昂贵的 Reactive Extensions `IObservable` 函数

c# - 如何使用 C# 执行协程?