Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using async in non-async method

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.

like image 386
Control Freak Avatar asked Dec 15 '14 05:12

Control Freak


People also ask

Can I use async await in normal function?

The resolved value of the promise is treated as the return value of the await expression. Use of async and await enables the use of ordinary try / catch blocks around asynchronous code. Note: The await keyword is only valid inside async functions within regular JavaScript code.

Can we use await in non async method C#?

Every now and then you'll find yourself in a synchronous method (i.e. one that doesn't return a Task or Task<T> ) but you want to call an async method. However, without marking the method as async you can't use the await keyword.

Can we call asynchronous method from another synchronous method?

Use the Result property on the asynchronous Task, like so: // Synchronous method. void Method()

What happens if I call async method without await?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.


1 Answers

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).

like image 194
i3arnon Avatar answered Sep 21 '22 02:09

i3arnon