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

时间:2015-10-30 05:30:42

标签: c# .net asynchronous task-parallel-library

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

MyInternalLibraryClass::DoSomething(const std::vector<MyObjectImpl>& objs);

如果取消了取消令牌,则尽管通过使用语句激活,但任务中的一次性用品不会被处置。

以下程序说明了问题:

 obj.asyncMethod().Wait(myCancelToken)

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

2 个答案:

答案 0 :(得分: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--;
    }
}

注意一旦长时间运行的操作完成,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);
    }
}

<强>输出
泄漏的实例= 1
处置资源。泄漏的实例= 0

答案 1 :(得分:4)

  

似乎Wait方法只是在取消时杀死任务线程而不是在该线程中触发异常

你错了,当你取消发生的唯一事情是你停止等待Wait(myCancelToken)完成时,任务仍然在后台运行。

要取消后台任务,您必须将取消令牌传递到链中的所有方法。如果您希望最内层(长时间运行的层)提前停止,则代码必须在其代码中调用token.ThrowIfCancellationRequested()