.NET 中的内存泄漏在执行异步超过同步时
Memory leaks in .NET when doing async over sync
我有一种情况,我必须同步调用一个异步方法,它是这样完成的:
obj.asyncMethod().Wait(myCancelToken)
如果取消标记被切换,尽管通过 using 语句激活,任务中的一次性标记将不会被释放。
下面的程序说明了这个问题:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LeakTest {
class Program {
static void Main(string[] args) {
try {
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask().Wait(timeout.Token);
} catch (OperationCanceledException error) {
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadKey();
}
static async Task LongRunningTask() {
using (var resource = new DisposableResource()) {
await Task.Run( () => Thread.Sleep(1000));
}
}
public class DisposableResource : IDisposable {
public static int Instances = 0;
public DisposableResource() {
Instances++;
}
public void Dispose() {
Instances--;
}
}
}
}
似乎 Wait 方法只是在取消时杀死任务线程,而不是在该线程内触发异常并让它自然终止。问题是为什么?
您取消了 Wait(timeout.Token)
返回的任务,而不是 LongRunningTask
返回的任务,如果您想取消那个任务,请将令牌传递给 Task.Run
并使用 await Task.Delay
而不是 Thread.Sleep
并将令牌也传递到那里。
static void Main(string[] args)
{
try
{
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask(timeout.Token).Wait();
}
catch (AggregateException error)
{
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadLine();
}
static async Task LongRunningTask(CancellationToken token)
{
using (var resource = new DisposableResource())
{
await Task.Run(async () => await Task.Delay(1000, token), token);
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
}
}
注意 一旦长 运行 操作完成,using 语句仍将处理资源。 运行 这个例子:
static void Main(string[] args)
{
try {
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask().Wait(timeout.Token);
} catch (OperationCanceledException error) {
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadKey();
}
static async Task LongRunningTask()
{
using (var resource = new DisposableResource())
{
await Task.Run(() => Thread.Sleep(1000));
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
Console.WriteLine("Disposed resource. Leaked Instances = {0}", Instances);
}
}
输出
泄漏实例 = 1
处置的资源。泄漏实例 = 0
It seems Wait method just kills the task thread on cancellation instead of triggering an exception within that thread
你错了,当你取消时,唯一发生的事情就是你停止等待 Wait(myCancelToken)
完成,任务仍在后台 运行。
为了取消后台任务,您必须将取消令牌传递到链下的所有方法中。如果您希望最内层(长 运行 层)尽早停止,该代码必须在其整个代码中调用 token.ThrowIfCancellationRequested()
。
我有一种情况,我必须同步调用一个异步方法,它是这样完成的:
obj.asyncMethod().Wait(myCancelToken)
如果取消标记被切换,尽管通过 using 语句激活,任务中的一次性标记将不会被释放。
下面的程序说明了这个问题:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LeakTest {
class Program {
static void Main(string[] args) {
try {
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask().Wait(timeout.Token);
} catch (OperationCanceledException error) {
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadKey();
}
static async Task LongRunningTask() {
using (var resource = new DisposableResource()) {
await Task.Run( () => Thread.Sleep(1000));
}
}
public class DisposableResource : IDisposable {
public static int Instances = 0;
public DisposableResource() {
Instances++;
}
public void Dispose() {
Instances--;
}
}
}
}
似乎 Wait 方法只是在取消时杀死任务线程,而不是在该线程内触发异常并让它自然终止。问题是为什么?
您取消了 Wait(timeout.Token)
返回的任务,而不是 LongRunningTask
返回的任务,如果您想取消那个任务,请将令牌传递给 Task.Run
并使用 await Task.Delay
而不是 Thread.Sleep
并将令牌也传递到那里。
static void Main(string[] args)
{
try
{
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask(timeout.Token).Wait();
}
catch (AggregateException error)
{
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadLine();
}
static async Task LongRunningTask(CancellationToken token)
{
using (var resource = new DisposableResource())
{
await Task.Run(async () => await Task.Delay(1000, token), token);
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
}
}
注意 一旦长 运行 操作完成,using 语句仍将处理资源。 运行 这个例子:
static void Main(string[] args)
{
try {
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask().Wait(timeout.Token);
} catch (OperationCanceledException error) {
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadKey();
}
static async Task LongRunningTask()
{
using (var resource = new DisposableResource())
{
await Task.Run(() => Thread.Sleep(1000));
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
Console.WriteLine("Disposed resource. Leaked Instances = {0}", Instances);
}
}
输出
泄漏实例 = 1
处置的资源。泄漏实例 = 0
It seems Wait method just kills the task thread on cancellation instead of triggering an exception within that thread
你错了,当你取消时,唯一发生的事情就是你停止等待 Wait(myCancelToken)
完成,任务仍在后台 运行。
为了取消后台任务,您必须将取消令牌传递到链下的所有方法中。如果您希望最内层(长 运行 层)尽早停止,该代码必须在其整个代码中调用 token.ThrowIfCancellationRequested()
。