AsyncEx
AsyncEx copied to clipboard
Sometimes AsyncContext would not stop
I understand that AsyncContext would not stop until all tasks are finished. I want to find out which tasks are out running. But I didn't find out a good way. Both the Tasks view and Parallel Stacks view didn't help. Then I found something strange, the _outstandingOperations is not matching with _queue.Count
I read the source but didn't find any clue. Can you help me?
I want to find out which tasks are out running.
Unfortunately, AsyncEx can't help you with that, since it doesn't have that information.
the _outstandingOperations is not matching with _queue.Count
The _outstandingOperations tracks the number of "operations" that have been registered with the SynchronizationContext. Depending on how you use AsyncContext, it may use 0 or 1 internally, and the rest represent async void methods. The _queue is the continuations that have been queued to the AsyncContext. These track different kinds of asynchronous operations and aren't supposed to be the same.
If the screenshot you posted is at a time when you expected the AsyncContext to be complete, then you can conclude that since _outstandingOperations is 3, then there are at least two async void methods that have not completed.
By async void do you mean async void Func(...)? I see _outstandingOperations is increased from AsyncContext.TaskScheduler.Enqueue(Task). These Tasks are mainly completion callbacks from other threads than the main thread, for example, timer or socket callbacks.
private void Enqueue(Task task, bool propagateExceptions)
{
OperationStarted();
task.ContinueWith(_ => OperationCompleted(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, _taskScheduler);
_queue.TryAdd(task, propagateExceptions);
tSynchronizationContext(WinFormsSynchronizationContext).
}
private void OperationCompleted()
{
var newCount = Interlocked.Decrement(ref _outstandingOperations);
if (newCount == 0)
_queue.CompleteAdding();
}
public void Execute()
{
SynchronizationContextSwitcher.ApplyContext(_synchronizationContext, () =>
{
var tasks = _queue.GetConsumingEnumerable();
foreach (var task in tasks)
{
_taskScheduler.DoTryExecuteTask(task.Item1);
// Propagate exception if necessary.
if (task.Item2)
task.Item1.WaitAndUnwrapException();
}
});
}
This is where _outstandingOperations is mainly increased and decreased (the other 2 is in AsyncContext.Run). Seems _outstandingOperations is highly bound with _queue. Each time _outstandingOperations is increased a task will be added to the queue. The task will be then popped from the queue and executed. When it is complete, the counter decreases.
OK I found out that AsyncVoidMethodBuilder does call SynchronizationContext.OperationStarted.
Anyway, I decide to forcefully decrease _outstandingOperations when I believe my program should exit, using reflection haha
Ah, I forgot that it's also used in Enqueue.
I decide to forcefully decrease _outstandingOperations when I believe my program should exit, using reflection
I strongly recommend against this. I recommend finding what code isn't completing and ensure it completes, instead.