在执行同步异步时,.NET中的内存泄漏

Her*_*eld 0 .net c# asynchronous task-parallel-library

我有一种情况,我必须同步调用异步方法,并按如下方式完成:

 obj.asyncMethod().Wait(myCancelToken)
Run Code Online (Sandbox Code Playgroud)

如果取消令牌被切换,则尽管通过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--;
                    }
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

似乎Wait方法只是在取消时杀死任务线程,而不是在该线程内触发异常并让它自然终止.问题是为什么?

Ned*_*nov 5

您已经取消了返回的任务,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--;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,一旦长时间运行的操作完成,using statment仍将处置资源.运行此示例:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出
泄漏实例= 1
处置资源.泄漏的实例= 0