CancellationToken 泄漏内存
CancellationToken leaking memory
我有一个计时器,每 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 代码中存在 is/was 问题,异步函数注册取消,但只有在出现异常时才会处理。正如您在大多数情况下注意到的那样,它只会增加列表。
此问题已在某个时间 last year in .NET core v2.x 得到修复(不确定它是否会在 .NET 标准中得到修复,我使用的是 4.6.1,但它仍然是一个问题)。
我解决它的一种方法是围绕我需要的异步函数编写自己的包装器。
您也可以调用非异步函数,(但您失去了异步函数为您提供的 'cancel' 功能)。
我有一个计时器,每 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 代码中存在 is/was 问题,异步函数注册取消,但只有在出现异常时才会处理。正如您在大多数情况下注意到的那样,它只会增加列表。
此问题已在某个时间 last year in .NET core v2.x 得到修复(不确定它是否会在 .NET 标准中得到修复,我使用的是 4.6.1,但它仍然是一个问题)。
我解决它的一种方法是围绕我需要的异步函数编写自己的包装器。
您也可以调用非异步函数,(但您失去了异步函数为您提供的 'cancel' 功能)。