I have a continuous async method that is used for things like polling resources and message queues.
private async Task MonitorAsync(CancellationToken cancelToken)
{
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(100);
// Poll stuff and take action
}
}
This is running in the UIContext (simplifies concurrency issues).
AppViewModel()
{
MonitorAsync(); // Starts the Monitor
}
What I've observed is in certain extreme conditions, this async method will simply stop running (e.g. application stops processing messages). For example, if too much CPU-bound code runs in the UIContext. Additionally, I have a few Monitors running, and only some of them die.
I'll grant that so far I've only seen this occur in a scenario that fundamentally needs to be addressed, but I'm still concerned about the fact that it would still be possible in edge cases.
As a workaround, I'll probably need to add a timer and restart the Monitor if it appears to have died.
Some additional notes:
- It's not throwing an exception. Hooking into UnobservedTaskException or wrapping in try/catch confirms this.
- It's definitely not stuck on an internal await. I added a simple flag to confirm, and it gets to "await Task.Delay(100)", but it never comes back.
QUESTIONS
- What could cause this to happen?
- How would I go about debugging this? What object could I inspect to, for example, see a list of running async methods in a given context?
I suspect the Visual Studio 2015 "Tasks Window" should be listing to all the async methods, but it is blank. It says "No tasks to display." I've never seen it display anything.
MORE INFORMATION:
I'm certain it didn't throw an exception or get stuck in an indefinite wait. The symptom appears that the Task is not running any more (or is delayed for an incredibly long time). It also appears to continue running some of the Tasks, and in fact the ones are still running are newer ones.
I have a theory that, in this rare circumstance, there is preference given to the most recently created Tasks. It gives whatever limited time it can to the recently created ones, and the old ones effectively aren't run any more.
It would help if I could access a list of Tasks. Then I could confirm if that is the case. I can do that for Threads, but so far I haven't found out how to do that for Tasks.
UPDATE 6/7/2016:
It seems the Task in question is actually still running. It's just significantly delayed. For example, if I run three Tasks running this method, then recreate the edge case - two of them are running fine (await resumes after 100 ms), but one of them (the oldest one) takes anywhere from 2 seconds to 20 seconds (sometimes more). So it seems the scheduler doesn't try to fairly distribute limited processing availability.
Per recommendation, I will split into two clearly stated questions:
- How to access list of Tasks
- How scheduler should behave
PARTIAL ANSWER
1. What could cause this to happen?
I wasn't able to find out how the scheduler manages Tasks, but observations show that when the scheduler falls behind, the resulting Task scheduling isn't even close to fair. Some Tasks may be significantly delayed, and the bias does seem towards newer Tasks. For example, in one test with three identical Tasks that expect to get processing every 100 ms:
- The most recent two were still running consistently every ~150 ms
- The oldest one was delayed anywhere from 500 ms to 30 seconds
2. How would I go about debugging this? What object could I inspect to, for example, see a list of running async methods in a given context?
I wasn't able to figure out how to use object inspection to get a list of Tasks, but the easiest way to get a list of Tasks, or to see a queue. The "Parallel Stacks" or "Tasks" windows in VS2015 are great tools to see all Tasks, however limitations:
- It does not list Tasks from async methods in Windows 7
- It won't give you any information about queue or priority. You can always add your own timers to guess at it.