c# - CancellationToken 泄漏内存
问题描述
我有一个计时器,每 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,(并且不断攀升)......
如果我只替换下面的代码而不替换其他代码
...
_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 中得到了修复,(不确定它是否会在 .NET 标准中得到修复,我使用的是 4.6.1,但它仍然是一个问题)。
我解决它的一种方法是围绕我需要的异步函数编写自己的包装器。
你也可以调用非异步函数,(但是你失去了异步函数给你的“取消”功能)。
推荐阅读
- xml - 按参数值拆分谷歌 XML 项目
- python - 意外的关键字参数
- javascript - 基于道具的元素的条件渲染顺序?
- spring - Spring Security 在运行时动态添加/删除 antMatchers 和角色
- c++ - 我在构造函数中使用什么类型来初始化由无名枚举定义的成员变量?
- javascript - 有没有办法在标准的 javascript 函数中创建和访问方法?
- python - 使用python根据目录中的文件名读取文件的优化方法
- sqlite - sqlite 公用表表达式
- python-3.x - 第二个 QSizeGrip 到 QStatus 栏
- php - 提交带有某些俄语字符的表单时出现 404 错误