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

时间:2012-02-13 12:12:32

标签: 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),那么它就是。

另外,如果我使用冷可观察源作为源,那么无论如何都要使用GC。

为什么Window功能具有TimeSpan特殊功能,如何在使用时确保foo为GC?

1 个答案:

答案 0 :(得分:2)

这对我来说是正确的。

您错过了对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();

我希望有所帮助。另请查看我的Rx博客系列介绍中的Lifetime Management帖子。

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