14

Lets say I only want one method to run in async.

So I have an async method like below:

public async Task Load(){
    Task task1 = GetAsync(1);
    Task task2 = GetAsync(2);
    Task task3 = GetAsync(3);
    var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
    var data2 = await task2;
    var data3 = await task3;
    ..process data..
}

And then I'm trying to call that async method in another method as a task, and would like for it to wait until that particular piece of async code is done. The problem is it's not. When it reaches the first await in Load() it just doesn't finish loading. The debugger goes blank and gives no other error.

Is an async method able to be called from a non async method, like this? There is a reason I do not need this particular task to be async, but the Load() function I do.

public void GetSomethingElse(){
    var task1 = Load().Wait();      
}

How is this possible?


I tried even changing the Load() method to use var data = task1.Wait(), etc. instead of await, still no difference, no matter which way I try. If anyone can help it would be appreciated.

6
  • try: var data1 = await Task.Run(() => GetAsync(1)); Commented Dec 15, 2014 at 5:58
  • blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx Commented Dec 15, 2014 at 6:00
  • @JohnWoo I did try var data1 = task1.Wait(); would doing it your way be any different? Commented Dec 15, 2014 at 6:01
  • I can see one difference: John's method guarantees that a worker thread is spun up. Did you check out the article I linked? Commented Dec 15, 2014 at 6:02
  • 1
    @JohnWoo I used your suggestion in GetSomethingElse() instead and it works. If you would like to make an answer, I will accept. Thank you btw. Commented Dec 15, 2014 at 6:12

3 Answers 3

11

You probably have a deadlock on your hands. You're blocking a thread using Wait() on a task that needs that thread to complete because there's a SynchronizationContext being used in ASP.Net (also in GUI environments).

You should use ConfigureAwait(false) to tell the awaiter not to capture that context. It's enough to do that on the first await since the rest would have no SynchronizationContext to capture:

public async Task Load()
{
    Task task1 = GetAsync(1);
    Task task2 = GetAsync(2);
    Task task3 = GetAsync(3);
    var data1 = await task1.ConfigureAwait(false);
    var data2 = await task2;
    var data3 = await task3;
    //..process data.
}

However, it's recommended to always use ConfigureAwait unless you want to capture the SynchronizationContext so a better standard is this:

public async Task Load()
{
    Task task1 = GetAsync(1);
    Task task2 = GetAsync(2);
    Task task3 = GetAsync(3);
    var data1 = await task1.ConfigureAwait(false);
    var data2 = await task2.ConfigureAwait(false);
    var data3 = await task3.ConfigureAwait(false);
    //..process data.
}

In your case, where you want to continue after all the tasks completed you should use Task.WhenAll instead of awaiting each task separately:

public async Task Load()
{
    await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false);
    // process data.
}

Note: doing sync over async is usually discouraged as it has no benefits (you're blocking a thread throughout the entire operation) and may cause deadlocks (like this one).

Sign up to request clarification or add additional context in comments.

3 Comments

What if GetAsync returns a variable? And are you saying that I'll still run into deadlocks using this method?
@ControlFreak No. ConfigureAwait solves that. Task.WhenAll is just more comfortable in this case.
@ControlFreak I've updated the answer to make that more clear.
2

I think you have a classic deadlock scenario. See this post for more details. When you have an await statement the current SynchronizationContext is stored before the await call and restored afterwards and the rest of the method posted to it. In GUI app there is only one thread associated with that context so the rest of the method is attemted to be executed on the GUI thread but it can't because Wait() is a blocking call which blocks the GUI thread.

Try this instead:

public async Task Load(){
    Task task1 = GetAsync(1).ConfigureAwait(false);
    Task task2 = GetAsync(2).ConfigureAwait(false);
    Task task3 = GetAsync(3).ConfigureAwait(false);

    var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
    var data2 = await task2;
    var data3 = await task3;
    ..process data..
}

If there are any awaits inside GetAsync you may have to add .ConfigureAwait(false) there as well.

Comments

1

You would change your Load function as following:

public async Task Load(){
    await new TaskFactory().StartNew(() =>
    {
        Task task1 = GetAsync(1);
        Task task2 = GetAsync(2);
        Task task3 = GetAsync(3);
        var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
        var data2 = await task2;
        var data3 = await task3;
        ..process data..
    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.