我正在尝试确保使用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?
答案 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章。