Code:
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SampleAsyncConsoleProgram
{
class Program
{
public static void ConsolePrint(string line)
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " ["
+ Thread.CurrentThread.ManagedThreadId.ToString() + "] > " + line);
}
static readonly IEnumerable<string> s_urlList = new string[]
{
"website1",
"website2",
"website3",
"website4"
};
static Task Main() => DownloadWebsites();
static async Task DownloadWebsites()
{
ConsolePrint("Main program: Program started..");
var stopwatch = Stopwatch.StartNew();
ConsolePrint("Main program: Adding tasks to list..");
IEnumerable<Task> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url);
List<Task> downloadTasks = downloadTasksQuery.ToList();
ConsolePrint("Main program: Added tasks to list..");
while (downloadTasks.Any())
{
ConsolePrint("Main program: Checking if a task is completed.... followed by await...");
Task finishedTask = await Task.WhenAny(downloadTasks);
ConsolePrint("Main program: A task was completed..");
downloadTasks.Remove(finishedTask);
await finishedTask;
}
stopwatch.Stop();
ConsolePrint($"Main program: Program Completed Elapsed time: {stopwatch.Elapsed}\n Current time is " + DateTime.Now);
}
static async Task ProcessUrlAsync(string url)
{
ConsolePrint("Task: Starts downloading " + url);
await Task.Delay(5000); //represents async call to fetch url
ConsolePrint("Task: Sleeping for 10 sec.." + url);
Thread.Sleep(10000); //represents some long running blocking synchronous work and keeping thread busy...
ConsolePrint("Task: Wake up.." + url);
ConsolePrint("Task: Done Task.." + url);
}
}
}
Output:
13:39:24.567 [1] > Main program: Program started..
13:39:36.255 [1] > Main program: Adding tasks to list..
13:39:37.177 [1] > Task: Starts downloading website1
13:39:43.241 [1] > Task: Starts downloading website2
13:39:43.242 [1] > Task: Starts downloading website3
13:39:43.242 [1] > Task: Starts downloading website4
13:39:43.243 [1] > Main program: Added tasks to list..
13:39:43.243 [1] > Main program: Checking if a task is completed.... followed by await...
13:39:48.811 [5] > Task: Sleeping for 10 sec..website1
13:39:48.810 [7] > Task: Sleeping for 10 sec..website2
13:39:48.810 [4] > Task: Sleeping for 10 sec..website4
13:39:48.810 [6] > Task: Sleeping for 10 sec..website3
13:39:58.823 [4] > Task: Wake up..website4
13:39:58.826 [5] > Task: Wake up..website1
13:39:58.823 [6] > Task: Wake up..website3
13:39:58.826 [7] > Task: Wake up..website2
13:39:58.828 [5] > Task: Done Task..website1
13:39:58.828 [6] > Task: Done Task..website3
13:39:58.829 [7] > Task: Done Task..website2
13:39:58.828 [4] > Task: Done Task..website4
13:39:58.922 [7] > Main program: A task was completed..
13:39:58.923 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.923 [7] > Main program: A task was completed..
13:39:58.923 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.924 [7] > Main program: A task was completed..
13:39:58.924 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.924 [7] > Main program: A task was completed..
13:39:58.985 [7] > Main program: Program Completed Elapsed time: 00:00:22.6686293
Current time is 30/07/2021 13:39:58
I was expecting that - at at line 65 (in the code), Thread.Sleep(10000);
- each task should have independently blocked for 10 seconds (because I have used Thread.Sleep(10000)
which is a synchronous and blocking code).
However from the output above, it looks like the Thread.Sleep
(a blocking operation) is happening as just like multi-threading.
I understand that - async+await without Task.Run()
in the case of windows app uses the same thread (UI thread). And that Thread.Sleep
is a synchronous blocking operation.
So why is each
Task
not blocking for 10 seconds? And what would be the way to make eachTask
block for 10 seconds?I have been advised that this behaviour is related to synchronization context. So it pulls threads from the thread pool (unlike the case of windows forms app). What I want to ask then is that - does this mean that it will perform multi-threading (multiple threads running in parallel)?