0

I'm new to C# and for now I'm trying to understand async/await feautures. So I have created small sandbox app:

namespace sandbox
{
public class Test
{
    public async Task<string> GetItemsAsync()
    {
        var a = await Task1();
        var b = await Task2();
        var c = await Task3();

        return a + b + c;
    }

    public string GetItems()
    {
        return _T1() + _T2() + _T3();
    }

    private readonly int cycles = 100000000;

    private async Task<string> Task1()
    {
        return await Task.Factory.StartNew(_T1);
    }

    private async Task<string> Task2()
    {
        return await Task.Factory.StartNew(_T2);
    }

    private async Task<string> Task3()
    {
        return await Task.Factory.StartNew(_T3);
    }

    // long running operation
    private string _T1()
    {
        for (int i = 0; i < cycles; i++) ;
        for (int i = 0; i < cycles; i++) ;
        return "One";
    }

    // long running operation
    private string _T2()
    {
        for (int i = 0; i < cycles; i++) ;
        for (int i = 0; i < cycles; i++) ;
        return "Two";
    }

    // long running operation
    private string _T3()
    {
        for (int i = 0; i < cycles; i++) ;
        for (int i = 0; i < cycles; i++) ;
        return "Three";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var t = new Test();

        Console.WriteLine("Async");
        Stopwatch sw = new Stopwatch();
        sw.Start();
        var result = t.GetItemsAsync();
        Console.WriteLine(result.Result);
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);

        Console.WriteLine("Sync");
        sw.Restart();
        var strResult = t.GetItems();
        Console.WriteLine(strResult);
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);

        Console.ReadLine();
    }
}
}

But the result is weird:

Async
OneTwoThree
1754
Sync
OneTwoThree
1506

Async method runs longer than similar sync one. For me it's look like async methods runs synchronously but I cant figure out why.

10
  • 3
    Because your awaiting all of them syncrhonously. Commented Jun 10, 2016 at 13:53
  • 1
    by awaiting the Task's you are changing it from a Asynchronous to Synchronous, the await keyword translates a pause the current task and don't continue until the awaited task completes Commented Jun 10, 2016 at 13:54
  • 1
    This question appears to be a magnet for people who either (a) don't understand async/await or (b) can't explain them Commented Jun 10, 2016 at 13:58
  • 1
    @amergan, can you describe for us what you believed the await operator does? I am interested to know how people's intuitions about programming language constructs lead them astray. An await is an asynchronous wait; the intention was to give you the intuition that you will be waiting for the result of the task, but still doing other work on the current thread while you're waiting. In what ways did we fail to give you the correct intuition, and how could it be better? Commented Jun 10, 2016 at 14:11
  • 1
    @EricLippert for me await means: start job and wait in the background for the result. In the meantime do the rest job. e.g. await Task1 -> start Task1 and go further. Commented Jun 10, 2016 at 14:23

2 Answers 2

4

Because of this:

var a = await Task1();
var b = await Task2();
var c = await Task3();

Before you even start Task2 you have waited for Task1 to complete. So you're not running them in parallel, you're running them in sequence.

If you want to start all 3 tasks, then wait for them to complete you would have to change this method to this:

public async Task<string> GetItemsAsync()
{
    var t1 = Task1();
    var t2 = Task2();
    var t3 = Task3();

    var a = await t1;
    var b = await t2;
    var c = await t3;

    return a + b + c;
}

Or just the last part:

return (await t1) + (await t2) + (await t3);

Additionally, this code is an antipattern:

private async Task<string> Task3()
{
    return await Task.Factory.StartNew(_T3);
}

You don't need async/await here because you're not doing any more work in this method after your sub-task returns.

Instead simply rewrite this method (and its siblings) to this:

private Task<string> Task3()
{
    return Task.Factory.StartNew(_T3);
}
Sign up to request clarification or add additional context in comments.

3 Comments

What do you mean by "not started the tasks"? That is the responsibility of those 3 methods, which clearly start new tasks. It is not my responsibility to start a task if that task was returned back to me. That is the responsibility of whatever it was that returned it. Anyway, these tasks are clearly started, Task.Factory.StartNew.
Apologises i misread their code and didn't notice the starts inside the task methods
The op could elide async/await; I wouldn't go so far as to call it an anti-pattern, though. What is definitely an anti-pattern is the use of StartNew without specifying a task scheduler (should use Task.Run instead). Also, you could make a strong case that returning a StartNew/Run task is also an anti-pattern (synchronous code should have synchronous APIs; the caller should decide whether to push to the thread pool).
1

I suspect the intention was to make all three tasks do the job in parallel. That is one of options how it could be accomplished:

public async Task<string> GetItemsAsync()
{
    var a = Task1();
    var b = Task2();
    var c = Task3();

    return string.Concat(await Task.WhenAll(a, b, c));
}

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.