英文:
Why does this cancellable Task result in a memory leak?
问题
这个任务扩展会导致内存泄漏的原因是,在AsCancellable
方法中,它使用了TaskCompletionSource<T>
(缩写为TCS),而TCS 对于未完成的任务引用保留了对任务的引用,这可能会导致内存泄漏。在这种情况下,TCS 可能会保留对ConsoleHandler
方法创建的Task<string>
对象的引用,因为在注册取消回调和ContinueWith回调时,TCS 可能会保留对这些任务的引用。
为了解决内存泄漏问题,你可以尝试手动解除 TCS 的引用。可以在 AsCancellable
方法的最后,返回 tcs.Task
之后,将 tcs
设置为 null
,以确保不再保留对 TCS 的引用。这将允许.NET 垃圾收集器在不再需要时正确回收相关的对象。
以下是修改后的代码示例:
public static class TaskExtensions
{
public static Task<T> AsCancellable<T>(this Task<T> task, CancellationToken token)
{
if (!token.CanBeCanceled)
{
return task;
}
var tcs = new TaskCompletionSource<T>();
// This cancels the returned task:
// 1. If the token has been canceled, it cancels the TCS straightaway
// 2. Otherwise, it attempts to cancel the TCS whenever
// the token indicates cancelled
token.Register(() => tcs.TrySetCanceled(token),
useSynchronizationContext: false);
task.ContinueWith(t =>
{
// Complete the TCS per task status
// If the TCS has been cancelled, this continuation does nothing
if (task.IsCanceled)
{
tcs.TrySetCanceled();
}
else if (task.IsFaulted)
{
tcs.TrySetException(t.Exception!);
}
else
{
tcs.TrySetResult(t.Result);
}
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
var resultTask = tcs.Task;
tcs = null; // Release the reference to TCS
return resultTask;
}
}
这个修改应该能够解决内存泄漏问题。希望这对你有所帮助。
英文:
I have a program where part of it reads from the console for input from another program.
So I have a Task
that looks like this:
public async Task ConsoleHandler()
{
while (true)
{
try
{
string command = (await Console.In.ReadLineAsync().AsCancellable(
_cancellationTokenSource.Token))!;
//Process the command
}
catch (Exception ex)
{
//Do something to report then wait for next command
}
}
}
Normally Console.ReadLineAsync
is not a cancel-able task, so I used the code from this question to create the following task extension:
public static class TaskExtensions
{
public static Task<T> AsCancellable<T>(this Task<T> task,
CancellationToken token)
{
if (!token.CanBeCanceled)
{
return task;
}
var tcs = new TaskCompletionSource<T>();
// This cancels the returned task:
// 1. If the token has been canceled, it cancels the TCS straightaway
// 2. Otherwise, it attempts to cancel the TCS whenever
// the token indicates cancelled
token.Register(() => tcs.TrySetCanceled(token),
useSynchronizationContext: false);
task.ContinueWith(t =>
{
// Complete the TCS per task status
// If the TCS has been cancelled, this continuation does nothing
if (task.IsCanceled)
{
tcs.TrySetCanceled();
}
else if (task.IsFaulted)
{
tcs.TrySetException(t.Exception!);
}
else
{
tcs.TrySetResult(t.Result);
}
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
return tcs.Task;
}
}
The problem, when running this in .NET Core 6.0.10, is that memory grows until I run out of memory (this is running on an embedded Linux device). Removing the call to .AsCancellable(...)
removes the memory leak. This happens even if there is no input on the Console.
Why does this task extension cause a memory leak?
答案1
得分: 3
"AsCancellable
" 没有正确实现。它在 "token" 上注册了一个回调,但从不释放这个注册。因此,注册信息堆积在 "CancellationToken" 的内部结构中。
由于您正在使用.NET 6,您无需使用 "AsCancellable" 方法。您可以改为使用正确实现的 WaitAsync
API。
英文:
The AsCancellable
is not implemented correctly. It registers a callback on the token
, and never disposes the registration. So the registrations are piling up in the internal structures of the CancellationToken
.
Since you are using .NET 6, you have no need for the AsCancellable
method. You can use the WaitAsync
API instead, which is, of course, correctly implemented.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论