Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可观察。使用TimeSpan选择器生成时出现内存泄漏[使用TimeSpan时>15ms]_C#_System.reactive - Fatal编程技术网

C# 可观察。使用TimeSpan选择器生成时出现内存泄漏[使用TimeSpan时>15ms]

C# 可观察。使用TimeSpan选择器生成时出现内存泄漏[使用TimeSpan时>15ms],c#,system.reactive,C#,System.reactive,我正在调查Observable.Generate的使用情况,以创建一系列每隔一段时间采样的结果,使用msdn网站上的示例作为起点 不带TimeSpan选择器的以下代码不会出现内存泄漏: IObservable<string> obs = Observable.Generate(initialState: 1, condition: x => x < 1000,

我正在调查Observable.Generate的使用情况,以创建一系列每隔一段时间采样的结果,使用msdn网站上的示例作为起点

不带TimeSpan选择器的以下代码不会出现内存泄漏:

IObservable<string> obs = Observable.Generate(initialState: 1,
                                              condition: x => x < 1000,
                                              iterate: x => x + 1,
                                              resultSelector: x => x.ToString());
obs.Subscribe(x => Console.WriteLine(x));
TimeSpan timeSpan = TimeSpan.FromSeconds(1);
IObservable<string> obs = Observable.Generate(initialState: 1,
                                              condition: x => x < 1000,
                                              iterate: x => x + 1,
                                              resultSelector: x => x.ToString(),
                                              timeSelector: x => timeSpan);
obs.Subscribe(x => Console.WriteLine(x));

我是否错误地使用了此API?
我应该期望内存增长,还是这是一个反应性错误?

这在我看来确实是一个错误,或者至少在DefaultScheduler的“递归”调度实现中是一个混乱/不受欢迎的行为(这不是真正的递归,我指的是在调度程序中传递给预定操作的重载,这样您就可以安排继续)

您看到的可处置组件是通过调用DefaultScheduler.Schedule方法(此处第71行:)创建的

这里的其他尝试之所以失败,有几个原因。首先,一次性物品最终会被处置——但只有当Generate
OnComplete
OnErrors
完成时,才会对Generate在订阅时返回的
System.Reactive.AnonymousSafeObserver
进行清理

其次,如果您使用较短的
TimeSpan
(记住.NET计时器的最小分辨率无论如何都是15ms),那么Rx将优化计时器的使用,并在不使用计时器的情况下调用
QueueUserWorkItem
,这样就不会创建这些一次性物品

如果深入研究Generate的实现()您可以看到,它将初始调用返回的
IDisposable
传递给Schedule,并将其传递回观测者,观测者将一直保留到出错/完成为止。这防止了整个递归调用的结果链被收集,这意味着如果您确实需要取消,或者当清理发生时,每个Schedule都将被收集led action的一次性设备无法处理

您可以在下面直接使用DefaultScheduler的代码中看到相同的效果-最后一行中对
cancel
的引用足以导致泄漏。请确保使用发布版本,否则编译器将保留cancel直到方法结束

// ensure you are using a release build of this code
ManualResetEvent mre = new ManualResetEvent();
IDisposable cancel;
int maxCount = 20;

TimeSpan timeSpan = TimeSpan.FromSeconds(1);

Func<IScheduler, int, IDisposable> recurse = null;
recurse = (self, state) =>
{
    Console.WriteLine(state);

    if (state == maxCount)
    {
        mre.Set();
        return Disposable.Empty;
    }

    return self.Schedule(state + 1, timeSpan, recurse);
};

cancel = Scheduler.Default.Schedule(1, timeSpan, recurse);

mre.WaitOne();

// uncomment the following line, and you'll get the same leak
// leave it commented, and cancel reference is GC'd early and there's no leak
// if(cancel == null) Console.WriteLine("Hang on to cancel");
//确保您正在使用此代码的发布版本
ManualResetEvent mre=新的ManualResetEvent();
IDisposable取消;
int maxCount=20;
TimeSpan TimeSpan=TimeSpan.FromSeconds(1);
Func recurse=null;
递归=(自身,状态)=>
{
控制台写入线(状态);
如果(状态==maxCount)
{
mre.Set();
返回一次性。空;
}
返回self.Schedule(状态+1,时间跨度,递归);
};
cancel=Scheduler.Default.Schedule(1,timeSpan,recurse);
韦通先生();
//取消对以下行的注释,您将得到相同的泄漏
//保留注释,取消引用是GC的早期版本,并且没有泄漏
//if(cancel==null)Console.WriteLine(“挂起以取消”);

我使用Jetbrains dotMemory API进行内存转储以得出结论-我已经将上面的代码从这些API调用中剥离出来,但是如果您有该产品,这里有一个完整的要点,您将能够非常清楚地看到取消注释最后一行的影响:或者,您可以使用MS profiler API-我没有提到过到目前为止我大脑的工作区!

可能正在进行多个订阅。
是否可以在事件处理程序中观察到?
上的投票数令人费解,因为任何人都可以复制粘贴并运行此代码,并看到没有内存泄漏…)@supertopi取决于
timeSpan
的声明位置。>我是否错误地使用了此API?那么您是如何使用API的呢?此处的一条注释表明内存泄漏取决于您在何处声明TimeSpan。在这种情况下,您需要详细说明,以便其他人可以实际复制您的问题。我用一个简单的程序示例更新了这个问题。感谢您的任何想法和建议!非常感谢大家。“我还没有把它输入到我的大脑工作区”,从现在起我将使用这个短语。随着时间的流逝,可能会更频繁!事实上,你能帮我解决这个问题吗?我不知怎的被卡住了,虽然这应该很简单。
System.Reactive.Disposables StableCompositeDisposable.Binary
System.Reactive.Disposables SingleAssignmentDisposable
// ensure you are using a release build of this code
ManualResetEvent mre = new ManualResetEvent();
IDisposable cancel;
int maxCount = 20;

TimeSpan timeSpan = TimeSpan.FromSeconds(1);

Func<IScheduler, int, IDisposable> recurse = null;
recurse = (self, state) =>
{
    Console.WriteLine(state);

    if (state == maxCount)
    {
        mre.Set();
        return Disposable.Empty;
    }

    return self.Schedule(state + 1, timeSpan, recurse);
};

cancel = Scheduler.Default.Schedule(1, timeSpan, recurse);

mre.WaitOne();

// uncomment the following line, and you'll get the same leak
// leave it commented, and cancel reference is GC'd early and there's no leak
// if(cancel == null) Console.WriteLine("Hang on to cancel");