Cumbaja
Cumbaja

Reputation: 45

Performance impact of using async await when its not necessary

Let's assume I have these methods:

public async Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    return await service.GetAsync(); 
}

and

public Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    return service.GetAsync();  
}

Both options compile and work the same way. Is there any best practise as to which option is better of if one is faster then the other?

Or is it just some syntactic sugar?

Upvotes: 4

Views: 1239

Answers (1)

Fabio
Fabio

Reputation: 32445

In the first method compiler will generate "state machine" code around it and execution will be returned to the line return await service.GetAsync(); after task will be completed. Consider example below:

public async Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    // Here execution returns to the caller and returned back only when Task is completed.
    Something value = await service.GetAsync(); 

    DoSomething();

    return value;        
}

The line DoSomething(); will be executed only after service.GetAsync task is completed.

Second approach simply starts execution of service.GetAsync and return correspondent Task to the caller without waiting for completion.

public Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    Task<Something> valueTask = service.GetAsync(); 

    DoSomething();

    return valueTask;        
}

So in the example above DoSomething() will be executed straight after line Task<Something> valueTask = service.GetAsync(); without waiting for completion of task.

Executing async method on the another thread depend on the method itself.

If method execute IO operation, then another thread will be only waste of the thread, which do nothing, only waiting for response. On my opinion async - await are perfect approach for IO operations.

If method GetAsync contains for example Task.Run then execution goes to the another thread fetched from thread pool.

Below is short example, not a good one, but it show the logic a tried to explain:

static async Task GetAsync()
{
    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine($"Iterate GetAsync: {i}");
        await Task.Delay(500);
    }
}

static Task GetSomethingAsync() => GetAsync();

static void Main(string[] args)
{
    Task gettingSomethingTask = GetSomethingAsync();

    Console.WriteLine("GetAsync Task returned");

    Console.WriteLine("Start sleeping");            
    Thread.Sleep(3000);            
    Console.WriteLine("End sleeping");

    Console.WriteLine("Before Task awaiting");
    gettingSomethingTask.Wait();
    Console.WriteLine("After Task awaited");

    Console.ReadLine();
}

And output will be next:

Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited

As you can see executing of GetAsync starts straight after calling it.

If GetSomethingAsync() will be changed to the:

static Task GetSomethingAsync() => new Task(async () => await GetAsync());

Where GetAsync wrapped inside another Task, then GetAsync() will not be executed at all and output will be:

GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited

Of course you will need to remove line gettingSomethingTask.Wait();, because then application just wait for task which not even started.

Upvotes: 2

Related Questions