Async Task WaitingForActivation Method

I found a very confusing behavior of async methods. Consider the following console application:

 private static int _i = 0; private static Task<int> _calculateTask = Task.FromResult(0); private static int _lastResult = 0; static void Main(string[] args) { while (true) { Console.WriteLine(Calculate()); } } private static int Calculate() { if (!_calculateTask.IsCompleted) { return _lastResult; } _lastResult = _calculateTask.Result; _calculateTask = CalculateNextAsync(); return _lastResult; } private static async Task<int> CalculateNextAsync() { return await Task.Run(() => { Thread.Sleep(2000); return ++_i; }); } 

As expected, after starting it first prints a bunch of 0s, then those, two, etc.

In contrast, consider the following fragment of a UWP application:

 private static int _i = 0; private static Task<int> _calculateTask = Task.FromResult(0); private static int _lastResult = 0; public int Calculate() { if (!_calculateTask.IsCompleted) { return _lastResult; } _lastResult = _calculateTask.Result; _calculateTask = CalculateNextAsync(); return _lastResult; } private static async Task<int> CalculateNextAsync() { return await Task.Run( async() => { await Task.Delay(2000); return ++_i; }); } private void Button_Click(object sender, RoutedEventArgs e) { while( true) { Debug.WriteLine(Calculate()); } } 

Although the two differ in only one small detail, the UWP fragment simply continues to print 0, and the task state in the if simply remains Waitingforactivation . In addition, the problem can be fixed by removing async and await from CalculateNextAsync :

 private static Task<int> CalculateNextAsync() { return Task.Run(async () => { await Task.Delay(2000); return ++_i; }); } 

Now everything works the same as in the console application.

Can someone explain the reason why the behavior in the Console is different from the UWP application? And why does the task remain as c in the case of a UWP application?

Update

I returned to this question again, but found that the originally accepted answer is not distributed - the UWP code never reaches .Result , it just keeps checking IsCompleted , which returns false , therefore _lastResult returned. What does it mean that Task has an AwaitingActivation state when it should end?

Decision

I found out that the reason is that the active while wait while prevents the continuation of await from ever capturing the UI thread, which leads to a deadlock situation.

+5
source share
1 answer

Based on the code in a UWP application, there is no need to hold down the _calculateTask button. Just an await task.

Here is the updated code

 private static int _i = 0; private static int _lastResult = 0; public async Task<int> Calculate() { _lastResult = await CalculateNextAsync(); return _lastResult; } //No need to wrap the code in a Task.Run. Just await the async code private static async Task<int> CalculateNextAsync() await Task.Delay(2000); return ++_i; } //Event Handlers allow for async void private async void Button_Click(object sender, RoutedEventArgs e) { while( true) { var result = await Calculate(); Debug.WriteLine(result.ToString()); } } 

ORIGINAL RESPONSE

You mix async / wait and block calls like .Result in the UWP application, which causes a dead end due to its one chunk SynchronizationContext. Console applications are an exception to this rule, so it works there, and not in a UWP application.

The root cause of this deadlock is due to the way that handle contexts wait. By default, when an unfinished task is expected, the current "context" is fixed and used to resume the method when the Task completes. This "context" represents the current SynchronizationContext, if only its null, in which case its current TaskScheduler. GUIs and ASP.NET Applications have a SynchronizationContext, which only allows one piece of code to run at a time. When the wait completes, it attempts to execute the remainder of the asynchronous method into the captured context. But in this context, there is already a thread that (synchronously) waits for the completion of the asynchronous method. Theyre each waiting for the other, causing a dead end.

Please note that console applications do not cause this deadlock. They have a thread pool SynchronizationContext instead of using a SynchronizationContext once, so when the wait completes, it schedules the remainder of the asynchronous method in the thread pool thread. This method that completes its return task, and theres no dead end. This difference in behavior can be confusing when programmers write a test console program, observe partially asynchronous code, as expected, and then move the same code to the GUI or ASP.NET application where it blocks.

Link Async / Await - Best Practices in Asynchronous Programming

+6
source

Source: https://habr.com/ru/post/1272927/


All Articles