我有一个计时器,每 2 秒启动 2 个任务...我在一个简单的列表中跟踪这些任务(这样我可以在停止应用程序时等待它们完成)。
这些任务有效地进入数据库,运行几次更新并完成。
任务本身的运行时间不会超过一秒
...
// global variables to keep track of the running tasks.
List<Task> _tasks = new List<Task>();
CancellationTokenSource _cts = new CancellationTokenSource();
// in the timer function
private void Timer(object sender, ElapsedEventArgs e)
{
_tasks.Add( Foo1Async(_cts.Token) );
_tasks.Add( Foo2Async(_cts.Token) );
// remove the completed ones
_tasks.RemoveAll(t => t.IsCompleted);
}
...
几分钟后,内存从 40Mb 增加到 130Mb,(并且还在攀升)...
如果我更换only下面的代码和没有别的
...
_tasks.Add( Foo1Async( CancellationToken.None)) );
_tasks.Add( Foo2Async( CancellationToken.None)) );
...
内存保持稳定在 40Mb,永远不会增加。
有几点
- 我在测试期间不会取消任何任务(它们都会运行完成)。
- 我在测试期间没有抛出任何异常。
- 我使用的数据库是Sqlite
- 在这两个测试中,我做了完全相同的工作,唯一的区别是使用的 CancellationToken。
- 即使我等待很长时间,记忆似乎也永远不会被释放。
- 定时器是一个
System.Timers.Timer
我只有在处理完计时器事件后才重新启动它。
- 我不使用任何其他代币(或代币来源)
- 我不注册任何令牌取消。
如果我拍摄内存快照的数量CancellationCallbackInfo
似乎是问题所在,但我不知道它们从哪里来以及如何释放它们。
查看.NET源代码,回调用于CancellationTokenSource
但我不太确定如何添加更多内容(或如何释放它们)。
关于我使用时可能导致内存泄漏的任何建议_cts.Token
与我使用时相比CancellationToken.None
某些 .NET DBCommand 代码存在问题,异步函数会注册取消,但只有在出现异常时才会进行处理。正如您所注意到的,在大多数情况下它只会增加列表。
这个问题曾经被修复过去年在 .NET core v2.x 中 https://github.com/dotnet/corefx/commit/297fcc33db4e65287455f6575684f24975688b53,(不确定它是否会在 .NET 标准中得到修复,我使用的是 4.6.1,但它仍然是一个问题)。
我解决这个问题的一种方法是围绕我需要的异步函数编写自己的包装器。
您还可以调用非异步函数(但您会失去异步函数提供的“取消”功能)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)