2

I was playing with Async and made a sample program below. The sample program compares time spent for synchronous and asynchronous operations

I was expecting that async operations will take lesser time, considering that tasks run in parallel on background thread. But the program below takes equal time for both sync and async operations. I think I am missing something, but everywhere I get the following example to create async task and run them

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncTest {
    class Program {

    private static long max = 100;

    static void Main(string[] args) {
        var startTime = DateTime.Now;
        Console.WriteLine("Start Process at");

        //Normal();    //takes about 20 secs
        MainAsync().Wait();     //takes about 20 secs

        Console.WriteLine($"Total Time Taken {DateTime.Now.Subtract(startTime).TotalMilliseconds} ms");
        Console.ReadKey();

    }

    static void Normal() {
        for (long i = 0; i <= max; ++i) {
            Thread.Sleep(200);
        }
    }

    static async Task MainAsync() {
        var runner = new Runner();

        for (int i = 0; i <= 100; ++i) {
            await runner.Run(i, max / 100);
        }
    }

}

public class Runner {
    public async Task Run(int idx, long max) {
        await Task.Run(() => {
            for (long i = 1; i <= max; ++i) {
                Console.WriteLine($"Task {idx} - {i}");
                Thread.Sleep(200);
            }
        });
    }
}

}

Any help what am I missing here ?

Naveen
  • 1,067
  • 2
  • 14
  • 36
  • 1
    Why would you think that something running on a background thread somehow takes less time than if it runs on the main thread? You're waiting on it to finish in both instances. – Preston Guillot Nov 28 '15 at 19:03
  • ? It should not matter if 20 second task is running on main thread or some other thread - it will still take 20 seconds... Are you looking for `Parallel.For`? – Alexei Levenkov Nov 28 '15 at 19:04
  • You still run one task as one task. Not matter on which thread you run it. if you split it into separate smaller tasks and run them together then it can speed things up. – M.kazem Akhgary Nov 28 '15 at 19:16
  • You should not use Wait method, if you want to run this in other thread. In this method, you can use async void or TPL. – Valentin Nov 28 '15 at 19:16
  • @AlexeiLevenkov I thought that Run method would be async, which would mean that as soon as I await it it would run in paraller with other tasks called similarly. I thought that the await runner.Run(...) should create multiple threads, which is clearly not happening because the tasks are run sequentially. – Naveen Nov 28 '15 at 19:27
  • @Naveen I've provided an answer, I hope it helps ;-) – Markus Safar Nov 28 '15 at 21:09
  • @Naveen I see - some reading about `await` like http://stackoverflow.com/questions/4057359/whats-the-new-c-sharp-await-feature-do and http://stackoverflow.com/questions/14455293/how-to-and-when-use-async-and-await may clarify your misunderstanding. Answers show `WhenAll` which is what you probably want in your code instead of sequentially awaiting in each iteration. – Alexei Levenkov Nov 29 '15 at 01:21

3 Answers3

1

The first problem is that you still run the tasks sequentially:

for (int i = 0; i <= 100; ++i) {
    await runner.Run(i, max / 100);
}

You start the task, wait until it is completed and only then continue iteration. Just change it to:

await Task.WhenAll(Enumerable.Range(0, 101).Select(i => runner.Run(i, max / 100)));

The second problem is using synchronous Thread.Sleep call. Replace Thread.Sleep(200) with await Task.Delay(200). It is ok to use Thread.Sleep to simulate some work but never use it with TPL (async-await) when you want just to wait.

Andrey Nasonov
  • 2,619
  • 12
  • 26
  • Thanks Andrey. This approach worked for me. I need to look into creating task more closely :) – Naveen Nov 29 '15 at 16:04
0

In my opinion you made the mistake of starting MainAsync with .Wait() which will block until the task is done. Within MainAsync you maybe assume to start runner a hundred times but in reality you wait (by using await) in each iteration of the for-loop to finish runner.Run(). And in the Run method of the Runner class you start Task where you are waiting (by using await) until it finishes too. You also use a synchronous Thread.Sleep call which should be replaced by await Task.Delay().

So in summary you are using await everywhere which results in waiting for the task until it has finished its execution.

What you could do e.g. is creating those Task instances, starting them and then use await to wait for them all.

As example a method that does "some heavy work":

static async Task DoHeavyWork(int idx, int max)
{
    for (long i = 1; i <= max; ++i)
    {
        Console.WriteLine($"Task {idx} - {i}");
        await Task.Delay(200);
    }
}

Now let us "start" them and until all of them are finished:

static void Main(string[] args)
{
    int max = 10;
    List<Task> tasks = new List<Task>();

    for (int i = 0; i <= max; ++i)
    {
        tasks.Add(DoHeavyWork(i, max));
    }

    Task.WaitAll(tasks.ToArray());
}

If you set a breakpoint at Task.WaitAll you will see, that you will hit this breakpoint before all tasks have finished their execution (what has been the idea behind this complete scenario in the first place ;-) ).

Markus Safar
  • 6,324
  • 5
  • 28
  • 44
-3

You can speed up things only if you:

1) have more then one processor

2) and can split the task to parallise it on the processors.

But in your example you are only waiting, nothing to parallise. So no speed up.

Dieter Meemken
  • 1,937
  • 2
  • 17
  • 22
  • Not clear how this post relates to code in question. Also `Sleep` can be parallelised perfectly fine - you can sleep as many threads as your process can handle without much impact (there are better ways, but having 10 threads sleeping in parallel is perfectly doable). – Alexei Levenkov Nov 29 '15 at 01:19