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 ?
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.
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 eg 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 ;-) ).
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.