Async/Await with threads (C# 7.2)

Asked

Viewed 1,801 times

9

I have this code and as you can see I created two examples, Parallel and Notparallel.

I expected both to return me 3000ms, because both should run async (2000 and 3000) and the total time would be 3000, but Nonparallel, is taking 5000ms, where it is the sum of the two, it seems that one waits for the other to finish.. even though you’re async.

static class Program
{
    static async Task<string> GetTaskAsync(int timeout)
    {
        Console.WriteLine("Task Thread: " + Thread.CurrentThread.ManagedThreadId);
        await Task.Delay(timeout);
        return timeout.ToString();
    }

    static async Task Main()
    {
        Console.WriteLine("Main Thread: " + Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine("Should be greater than 5000");
        await Watch(NotParallel);
        Console.WriteLine("Should be less than 5000");
        await Watch(Parallel);
    }

    public static async Task Parallel()
    {
        var res1 = GetTaskAsync(2000);
        var res2 = GetTaskAsync(3000);

        Console.WriteLine("result: " + await res1 + await res2);
    }

    public static async Task NotParallel()
    {
        var res1 = await GetTaskAsync(2000);
        var res2 = await GetTaskAsync(3000);

        Console.WriteLine("result: " + res1 + res2);
    }

    private static async Task Watch(Func<Task> func) {
        var sw = new Stopwatch();
        sw.Start();

        await func?.Invoke();

        sw.Stop();
        Console.WriteLine("Elapsed: " + sw.ElapsedMilliseconds);
        Console.WriteLine("---------------");
    }
}

Example of the Result:

inserir a descrição da imagem aqui

  • 1

    You want to explain how Parallel() and NotParallel() are different?

  • 1

    Give more details of what happens, and what is waiting to happen. see https://answall.com/q/1946/101 to understand that parallelizing does not guarantee better performance, in fact the performance is even worse, only ends earlier in cases that is correct, Maybe this already answers: https://answall.com/q/201998/101 It may also be useful: https://answall.com/q/175304/101 and https://answall.com/a/157440/101 Finally, how do you know that it is in parallel? I don’t think you are: https://answall.com/q/166032/101

  • @Maniero I am wrong to say that the two methods will have an identical behavior and that one cannot assume that one "is parallel" and the other " is unparalleled"? Ops didn’t see that you edited the comment.

  • @ramaral, dont see the Names of the methods.

  • @alerya You’re being sarcastic, right?(You’re being sarcastic, right?) :)

  • 1

    I will edit here and put the result @Maniero to illustrate better!

  • 1

    @exactly, wanted to understand why the behavior of the two are different?

  • Yeppers - Why one is Faster than other ?

  • @ramaral the alerya is a friend of Ukrania and we are discussing this subject, and decided to post here on PT tb. We work here in the same country.

  • 3

    Now that I’ve seen the results, I understand what you want to ask. Rephrase the question because it gives (me) to understand that you expected very different results but obtained close results.

  • @ramaral edited there, if you feel comfortable, can edit as well and put something that has in mind in the question to help me :)

  • 1

    Now I think you are well and I understand the reason for the question. Seeing this code, without seeing the result, I would say that the two methods would have an approximate execution time.

  • I can’t answer now, I never fully understood about Async, but I found the question interesting, today I don’t have time to stop and think, test, so I hope someone answers, or I’ll try to see next week, you can charge me. And in fact now you can understand the real question.

  • @Maniero thanks for the feedback, really me and the alerya spent a while and decided to ask the gurus here :)

  • @ramaral need to analyze better ;)

Show 10 more comments

2 answers

10


A common mistake is to think that asymchronism is synonymous with parallelism. The use of async is also not always well understood.

Use async, alone, it does not render the method (the code it executes) asynchronous. async only allows the word to be used await.
What allows certain code to be executed asynchronously is the Task.

The joint use of async/await allows write/mix sequentially synchronous and asynchronous code.
Each line of code is executed sequentially, when a line with await the execution of the method is suspended and the execution is returned to the calling code. When the asynchronous operation ends, the execution is resumed in the next line and proceeds sequentially.

This misunderstanding makes it used async in methods where it is not necessary:

private async Task<Resultado> MetodoAsync()
{
    ....
    ....
    return await opAsync();
}

There’s only need to use await if, in that method, you need to do something with the result and/or after the asynchronous operation has ended.
If not, do not use async, just return to Task.

private Task<Resultado> Metodo()
{
    ....
    ....
    return OpAsync();
}

When you’re ready to deal with the outcome, then yes, use async/await

That said, let’s look at each of the methods:

  • Notparallel()

    Each of the calls to GetTaskAsync() is made with await. When executing the first call(GetTaskAsync(2000)) the execution is released (the method returns immediately), waiting for it to end and then proceeding with the second call(GetTaskAsync(3000)). That is to say, GetTaskAsync(3000) is called only after GetTaskAsync(2000) have finished.

  • Parallel()

    How is it not used wait in the call to GetTaskAsync() the calls are executed immediately one after the other and the tasks are executed in "parallel" (simultaneous law-a).

I simplified the code to try to show that "parallelism" has, in this case, only to do with the moments (instants) in which each call to GetTaskAsync() is made.
In this situation(console application) only the method GetTaskAsync() needs to be declared async.
He is the one who needs to release the execution to allow it to be called again, in the method Parallel(), before ending the previous call.

static class Program
{
    static async Task<string> GetTaskAsync(int timeout)
    {
        Console.WriteLine("Task Thread: " + Thread.CurrentThread.ManagedThreadId);
        await Task.Delay(timeout);
        return timeout.ToString();
    }

    private static void Main()
    {
        Console.WriteLine("Main Thread: " + Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine("Should be greater than 5000");
        Watch(NotParallel);
        Console.WriteLine("Should be less than 5000");
        Watch(Parallel);
        Console.ReadKey();
    }

    public static void Parallel()
    {
        var res1 = GetTaskAsync(2000);
        var res2 = GetTaskAsync(3000);

        Console.WriteLine("result: " + res1.Result + " " + res2.Result);
    }

    public static void NotParallel()
    {
        var res1 = GetTaskAsync(2000).Result;
        var res2 = GetTaskAsync(3000).Result;

        Console.WriteLine("result: " + res1 + " " + res2);
    }

    private static void Watch(Action func)
    {
        var sw = new Stopwatch();
        sw.Start();

        func();

        sw.Stop();
        Console.WriteLine("Elapsed: " + sw.ElapsedMilliseconds);
        Console.WriteLine("---------------");
    }
}
  • 2

    I need to take a closer look, but I think that’s the way it is.

6

The sum in the NotParalel() is precisely why you are waiting for an execution to finish and then run the next one and finally return, on account of the await.

public static async Task NotParallel()
{
    //A atribuição de res1 deve esperar o resultado de GetTaskAsync()
    var res1 = await GetTaskAsync(2000);

    //Somente depois de res1 receber sua atribuição a de res2 deve esperar
    // o novo GetTaskAsync(3000)
    var res2 = await GetTaskAsync(3000);

    //Logo o resultado será no mínimo 5000ms porque ele parou as duas vezes
    Console.WriteLine("result: " + res1 + res2);
}

Now in parallel, see the difference:

public static async Task Parallel()
{
    //Dispara a atribuição de assíncrona de res1 e segue a execução
    var res1 = GetTaskAsync(2000);

    //Dispara a atribuição assíncrona de res2 e segue a execução
    var res2 = GetTaskAsync(3000);

    //Agora essa linha espera até que res1 e res2 tenham recebido
    //suas atribuições e como a maior espera é de res2, res1 já terá
    //recebido a sua antes e o tempo total de espera é de apenas 3000ms 
    Console.WriteLine("result: " + await res1 + await res2);
}

This is precisely why one method is called parallel and the other is not. This is the difference in running time between the two and where you will begin to give due importance to parallelism and asynchronous programming to get the best performance in time in your applications.

To further deepen the subject you can consult Microsoft’s own recommendations for this type and implementation in C#.

Asynchronous programming with Async and Await (C# and Visual Basic)

Asynchronous programming based on tasks

  • I guess that’s the way it is, I’ll take a closer look.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.