简体   繁体   中英

Dangling await and possible memory leaks in async programming

The flow containing await in .NET 4.5 and Async CTP 4.0 can be stuck due to various reasons, eg since the remote client has not responded. Of course, WaitForAny, when we wait also for some timeout task is an obvious solution for recovery of the high-level flow. Still, this does not solve all possible problems.

I have the following questions:

  1. What happens to the context of await which does not ever return? I understand that this will create memory leak. Am I right?

  2. How can I check either in debugger or using the respective API how many dangling " awaiter "s exist in the application?

  3. Is it possible to enumerate them globally?

  4. If 3. is correct, is it possible to force cancellation the tasks for these *await *s (ie to clean up)?

Note: In question 4 I don't ask about cancellation items to be used during explicit task creation. I mean the case when the task was created indirectly:

async Task<bool> SomeTask()
{
   await Something();
   ...
   return true;
}

Motivation for this question:

  1. Trying to avoid memory leaks
  2. Trying to complication of the code with too many cases involving cancellation tokens
  3. In many situations the timeout is not known in advance for each low-level Task, but the high-level flow can use just recovery approach: "We are stuck? Never mind, just clean up and let's start over".

1 What happens to the context of await which does not ever return?

I believe it will cause a memory leak (if you're await ing an I/O operation). It's best to always complete your Task s (and this means always having your async methods return sooner or later).

Update from svick's comment: There are situations where this would not cause a memory leak.

2 How can I check either in debugger or using the respective API how many dangling "awaiter"s exist in the application?

I'm not sure if there's an easy way to do this. I believe it should be possible to write a debugger plugin that uses SoS to find existing heap objects that match the pattern of the asynchronous state machines generated by the compiler.

But that's a lot of work for little benefit.

3 Is it possible to enumerate them globally?

Not with the normal APIs.

If 3 is correct, is it possible to force cancellation the tasks for these awaits (ie to clean up)?

Even if you could enumerate them at runtime (eg, via the profiling API), you can't "force" cancellation onto a task. Cancellation is cooperative.


The correct way to deal with this is with standard cancellation. The Task-based Asynchronous Pattern document specifies guidelines for cancelable async methods.

At the lowest level: Many async APIs in the BCL take an optional CancellationToken .

At a middle level: It's common to have an async method take an optional CancellationToken and just pass it on to other async methods.

At the highest level: It's easy to create a CancellationToken that will fire after a given time.

About questions 2 and 3 I have no real answer.

  1. I doubt it as long as the resources used in the task are properly disposed when the CLR disposes the task when the application terminates.

Having tasks around that never return is hardly ever a good thing. To avoid this and to answer point 4: tasks can be cancelled.

You need to create a cancellationtoken that you pass to the task. The task is responsible on his own to watch for the status of that cancellation token and throw an exception when cancelled. (Multiple tasks can be cancelled at once using the same token too.)

This article on MSDN shows you to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM