Async Always Waitingforactivation

Async always WaitingForActivation

For my answer, it is worth remembering that the TPL (Task-Parallel-Library), Task class and TaskStatus enumeration were introduced prior to the async-await keywords and the async-await keywords were not the original motivation of the TPL.

In the context of methods marked as async, the resulting Task is not a Task representing the execution of the method, but a Task for the continuation of the method.

This is only able to make use of a few possible states:

  • Canceled
  • Faulted
  • RanToCompletion
  • WaitingForActivation

I understand that Runningcould appear to have been a better default than WaitingForActivation, however this could be misleading, as the majority of the time, an async method being executed is not actually running (i.e. it may be await-ing something else). The other option may have been to add a new value to TaskStatus, however this could have been a breaking change for existing applications and libraries.

All of this is very different to when making use of Task.Run which is a part of the original TPL, this is able to make use of all the possible values of the TaskStatus enumeration.

If you wish to keep track of the status of an async method, take a look at the IProgress(T) interface, this will allow you to report the ongoing progress. This blog post, Async in 4.5: Enabling Progress and Cancellation in Async APIs will provide further information on the use of the IProgress(T) interface.

Status = WaitingForActivation using Async method

You need to await your CallApi method like this:

var getData = await CallApi("<api-url>", token.access_token);

This means that your calling method will also need to be async like this:

public static async Task MyCallingMethod(...)
{
...
var getData = await CallApi("<api-url>", token.access_token);
...
}

If your calling method must be synchronous, then consider calling your API like this:

public static void MyCallingMethod(...)
{
...
string getData = null;
Task.Run(async () =>
{
getData = await CallApi("<api-url>", token.access_token).ConfigureAwait(false);
}).GetAwaiter().GetResult();
...
}

Also, using HttpClient inside of a using statement can be very dangerous. Please check this article: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

If you noticed, I used ConfigureAwait(false) in my code sample. You can learn more about this in the following article: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

Task always on status Waiting for Activation (finding all serial devices)

You will need some changes in your code.

Instead of public static void GetAvailablePorts you should write public async Task GetAvailablePorts

Then later on instead of var devices = task.Result; you should write var devices = await task;

A good reference to understand the async / await pattern can be found here: https://markheath.net/post/async-antipatterns

Async & await always returns Waiting for completion

async-await are just keywords which tell the compiler to compile your code as an implementation of an IAsyncStateMachine and also wait for it to finish, both keywords need to be used in conjunction for this to work and they only work on Task objects. A Task represents a piece of work that is happening.

You must mark your SendEmail method as async, change the return type to Task<object> and await your emailHandler.SendEmail call.

Bottom line is, if you're going async then you must go async all the way up and await at some or many points, otherwise you begin looking at running asynchronous code synchronously which is kind of shooting yourself in the foot.

public static async Task<object> SendEmail(string apiKey, string senderEmail, string senderName, string recipientEmail, string recipientName, string subject, string content, bool html)
{
EmailHandler emailHandler = new EmailHandler();
var response = await emailHandler.SendEmail(apiKey, senderEmail, senderName, recipientEmail, recipientName, subject, content, html);

return response;
}

As always, Stephen Cleary is an excellent source for async knowledge.

Task keeps waiting for activation

You need to make your event handler async as well:

private async void btnLogin_Click(object sender, EventArgs e) {
try {
_Token = await Api.LoginAsync( tbUsername.Text, tbPassword.Text);

}
catch (Exception ex) {
ShowError(ex.Message);
}
}

Calling async method results in WaitingForActivation “Not yet computed” error

Not really sure this is your problem,

however, you are awaiting await dwnldKeybankListTask twice, and not awaiting dwnldAOCListTask at all and passing the task straight to dwnldKeybankList

Try this

void async void FunctiontHandler(S3Event s3Event, ILambdaContext context)
{
var bankPrefix = $"{prefix}KEYBANK_{Function.outputZipFileSuffix}/ST";

var bankList = await toEFS.ListDownloadObjectsAsync(bucket, bankPrefix , s3client);

var aocPrefix = $"{prefix}AOC_{Function.outputZipFileSuffix}/{AOCFilePrefix}";

var aocList = await toEFS.ListDownloadObjectsAsync(bucket, aocPrefix, s3client);

bankList.AddRange(aocList);
}

Also you are using async void, 9 times out of 10 you will actually want async Task. Additionally your variables names are fairly confusing

Async Task method WaitingForActivation

Based on the code in the UWP app there is no need for holding on to the _calculateTask. Just await the 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 ANSWER

You are mixing async/await and blocking calls like .Result in the UWP app which is causing a deadlock because of its one chunk SynchronizationContext. Console applications are an exception to that rule, which is why it works there and not in the UWP app.

The root cause of this deadlock is due to the way await handles
contexts. By default, when an incomplete Task is awaited, the current
“context” is captured and used to resume the method when the Task
completes. This “context” is the current SynchronizationContext unless
it’s null, in which case it’s the current TaskScheduler. GUI and
ASP.NET applications have a SynchronizationContext that permits only
one chunk of code to run at a time. When the await completes, it
attempts to execute the remainder of the async method within the
captured context. But that context already has a thread in it, which
is (synchronously) waiting for the async method to complete. They’re
each waiting for the other, causing a deadlock.

Note that console applications don’t cause this deadlock. They have a
thread pool SynchronizationContext instead of a one-chunk-at-a-time
SynchronizationContext, so when the await completes, it schedules the
remainder of the async method on a thread pool thread. The method is
able to complete, which completes its returned task, and there’s no
deadlock. This difference in behavior can be confusing when
programmers write a test console program, observe the partially async
code work as expected, and then move the same code into a GUI or
ASP.NET application, where it deadlocks.

Reference Async/Await - Best Practices in Asynchronous Programming



Related Topics



Leave a reply



Submit