简体   繁体   中英

TaskFactory inner task never get executed and always in WaitingForActivation status

I am using the TaskFactory to start new task using following code.

var task = Task.Factory.StartNew(async () =>
{
    await Task.Run(() =>
    {
        // Do API Call
        var saveResponse = doAPICall();
    }).ConfigureAwait(false);
}).Unwrap();

task.Wait();

The code for doAPICall() is like below which is calling an external API which returns the Task.

private string doAPICall()
{
    Task<string> response = client.FindBags(request);
    response.Wait(60000);

    if (response.Status == TaskStatus.RanToCompletion)
    {
        return response.Result;
    }
}

The issue occurs in doAPICall() function that Task 'response' status never change to RanToCompletion and it is always in WaitingForActivation status. I already tried to increase the wait timeout but still no luck. I am using the TaskFactory instead of Task because of in future I want to create custom TaskFactory for more control over scheduler and concurrency.

Is that something I am missing in my code that inner task never get executed?

Edit I modified the code of calling the doAPICall() according to comment of removing unnecessary thread but still no luck. :-(

var task = Task.Factory.StartNew(() =>
{
    // Do API Call
        var saveResponse = doAPICall();
});

task.Wait();

You're currently using sync-over-fake-async-over-fake-async-over-sync-over-async. That is seriously messed up.

Follow these guidelines:

  1. Don't use Task.Factory.StartNew . Ever. I am using the TaskFactory instead of Task because of in future I want to create custom TaskFactory for more control over scheduler and concurrency. Custom task schedulers don't work well with asynchronous code. You'll probably have to adopt more async-friendly solutions for scheduling/concurrency. More info on my blog .
  2. Don't block on asynchronous code. I see two Wait calls and a Result , both of which are serious red flags. More info on my blog .
  3. Don't use Task.Status in production code. It's OK for debugging, but you shouldn't ever have to use it for actual logic. There is always a better solution. More info on my blog .
  4. Only use Task.Run from the UI layer, not nested within helper/library code. In other words, use it to call methods, not implement methods. More info on my blog .

Working from the innermost method out:

private async Task<string> doAPICallAsync()
{
  Task<string> responseTask = client.FindBags(request);
  // Note: it would be far better to use a cancellation token here instead of a "timed wait".
  Task timeoutTask = Task.Delay(60000);
  Task completedTask = await Task.WhenAny(responseTask, timeoutTask);
  if (completedTask == responseTask)
    return await completedTask;
}

and your calling code becomes:

var saveResponse = await doAPICallAsync();

You are wrapping an async call in a sync call and then in two async calls. That is way to much, just use one async call and you will be fine.

private Task<string> doAPICallAsync()
{
    return client.FindBags(request);
}

If you need to do some work after client.FindBags(request) use this version:

private async Task<string> doAPICallAsync()
{
    var result = await client.FindBags(request);
    var someNewResult = //do something with result
    return someNewResult;
}

Then call it like this:

var result = await doAPICallAsync();

If you really need a sync version

private string doAPICall()
{
    return client.FindBags(request).Result;
}

And call it like this:

var result = doAPICall();

If you can, don't forget to add ConfigureAwait(false) after every await.

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