Consider the following code:
public async static Task<bool> Sleeper(int sleepTime)
{
Console.WriteLine("Sleeping for " + sleepTime + " seconds");
System.Threading.Thread.Sleep(1000 * sleepTime);
return true;
}
static void Main(string[] args)
{
Random rnd = new Random();
List<Task<bool>> tasks = new List<Task<bool>>();
Console.WriteLine("Kicking off tasks");
for (int i = 0; i < 3; i++)
{
tasks.Add(Sleeper(rnd.Next(10, 15)));
}
Console.WriteLine("All tasks launched");
Task.WhenAll(tasks);
int nComplete = 0;
foreach (var task in tasks)
{
if (task.Result)
nComplete++;
}
Console.WriteLine(nComplete + " Successful tasks");
}
Each task should sleep for a random amount of time (between 10-15 seconds). However my output looks like the following
Kicking off tasks
Sleeping for 12 seconds
Sleeping for 14 seconds
Sleeping for 12 seconds
All tasks launched
3 Successful tasks
Each "task" clearly waited for the previous task to be completed before starting (I also saw this when debugging and stepping through the code), why is this?
EDIT A lot of people have mentioned using Task.Delay which does work as expected. But what if I'm not doing anything like sleeping, just a lot of work. Consider a large do nothing loop
int s = 1;
for (int i = 0; i < 100000000000000; i++)
s *= i;
This still executes synchronously
async does not mean "runs on another thread". Stephen Toub's blog goes into a lot more detail, but under the hood the current SynchronizationContext and the operations performed determines if and when a task runs on a separate thread. In your case, Thread.Sleep doesn't do anything explicitly to run on a different thread, so it doesn't.
If you used await Task.Delay(1000 + sleepTime) instead of Thread.Sleep I think you'll find that things work as you expect, because Task.Delay is plugged into the async/await infrastructure, while Thread.Sleep isn't.
This is because you are using Thread.Sleep which is sleeping the thread which invokes Sleeper. async methods start on the thread which call them hence you are sleeping your main application thread.
In asynchronous code, you should be using Task.Delay like so:
public async static Task<bool> Sleeper(int sleepTime)
{
Console.WriteLine("Sleeping for " + sleepTime + " seconds");
await Task.Delay(1000 * sleepTime).ConfigureAwait(false);
return true;
}
async does not mean "run on another thread". I have an async intro that goes into detail about what async does mean.
In fact, Sleeper will generate a compiler warning informing you of the fact that it will run synchronously. It's a good idea to turn on "Warnings as Errors" for all new projects.
If you have CPU-bound work to do, you may run it on a thread pool thread and asynchronously wait for it to complete by using await Task.Run, as such:
public static bool Sleeper(int sleepTime);
...
for (int i = 0; i < 3; i++)
{
var sleepTime = rnd.Next(10, 15);
tasks.Add(Task.run(() => Sleeper(sleepTime)));
}
Related
This is the code that I wrote to better understand asynchronous methods. I knew that an asynchronous method is not the same as multithreading, but it does not seem so in this particular scenario:
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
//the line above just makes sure that the console output uses . to represent doubles instead of ,
ExecuteAsync();
Console.ReadLine();
}
private static async Task ParallelAsyncMethod() //this is the method where async parallel execution is taking place
{
List<Task<string>> tasks = new List<Task<string>>();
for (int i = 0; i < 5; i++)
{
tasks.Add(Task.Run(() => DownloadWebsite()));
}
var strings = await Task.WhenAll(tasks);
foreach (var str in strings)
{
Console.WriteLine(str);
}
}
private static string DownloadWebsite() //Imitating a website download
{
Thread.Sleep(1500); //making the thread sleep for 1500 miliseconds before returning
return "Download finished";
}
private static async void ExecuteAsync()
{
var watch = Stopwatch.StartNew();
await ParallelAsyncMethod();
watch.Stop();
Console.WriteLine($"It took the machine {watch.ElapsedMilliseconds} milliseconds" +
$" or {Convert.ToDouble(watch.ElapsedMilliseconds) / 1000} seconds to complete this task");
Console.ReadLine();
}
}
//OUTPUT:
/*
Download finished
Download finished
Download finished
Download finished
Download finished
It took the machine 1537 milliseconds or 1.537 seconds to complete this task
*/
As you can see, the DownloadWebsite method waits for 1.5 seconds and then returns "a". The method called ParallelAsyncMethod adds five of these methods into the "tasks" list and then starts the parallel asynchronous execution. As you can see, I also tracked the amount of time that it takes for the ExecuteAsync method to be executed. The result is always somewhere around 1540 milliseconds. Here is my question: if the DownloadWebsite method required a thread to sleep 5 times for 1500 milliseconds, does it mean that the parallel execution of these methods required 5 different threads? If not, then how come it only took the program 1540 milliseconds to be executed and not ~7500 ms?
I knew that an asynchronous method is not the same as multi-threading
That is correct, an asynchronous method releases the current thread whilst I/O occurs, and schedules a continuation after it's completion.
Async and threads are completely unrelated concepts.
but it does not seem so in this particular scenario
That is because you explicitly run DownloadWebsite on the ThreadPool using Task.Run, which imitates asynchronous code by returning a Task after instructing the provided delegate to run.
Because you are not waiting for each Task to complete before starting the next, multiple threads can be used simultaneously.
Currently each thread is being blocked, as you have used Thread.Sleep in the implementation of DownloadWebsite, meaning you are actually running 5 synchronous methods on the ThreadPool.
In production code your DownloadWebsite method should be written asynchronously, maybe using HttpClient.GetAsync:
private static async Task<string> DownloadWebsiteAsync()
{
//...
await httpClinet.GetAsync(//...
//...
}
In that case, GetAsync returns a Task, and releases the current thread whilst waiting for the HTTP response.
You can still run multiple async methods concurrently, but as the thread is released each time, this may well use less than 5 separate threads and may even use a single thread.
Ensure that you dont use Task.Run with an asynchronous method; this simply adds unnecessary overhead:
var tasks = new List<Task<string>>();
for (int i = 0; i < 5; i++)
{
tasks.Add(DownloadWebsiteAsync()); // No need for Task.Run
}
var strings = await Task.WhenAll(tasks);
As an aside, if you want to imitate an async operation, use Task.Delay instead of Thread.Sleep as the former is non-blocking:
private static async Task<string> DownloadWebsite() //Imitating a website download
{
await Task.Delay(1500); // Release the thread for ~1500ms before continuing
return "Download finished";
}
I have some problems getting a grasp on tasks and cancellation tokens. I've made a program that looks like this:
static void Main(string[] args)
{
CancellationTokenSource token = new CancellationTokenSource();
Stopwatch stop = new Stopwatch();
stop.Start();
for (int i = 0; i < 5; i++)
{
//Thread.Sleep(1000);
Task.Factory.StartNew(() => myLongTask(token.Token, (i + 1) * 1000));
}
while (true)
{
Thread.SpinWait(1000);
if (stop.ElapsedMilliseconds > 3000)
{
token.Cancel();
}
}
}
public static void myLongTask(CancellationToken token, int time)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancelled");
return;
}
var sw = Stopwatch.StartNew();
Console.WriteLine($"Task {time / 1000} started");
while (sw.ElapsedMilliseconds < time)
Thread.SpinWait(1000);
Console.WriteLine($"Task {time / 1000} ended");
}
I am running 5 tasks simultaneously (although when I don't include Thread.Sleep() the for loop seems to run before tasks are even started?). None of the tasks get ever cancelled when I run the program. Also what is bugging me is...what task am I really cancelling when calling token.Cancel()? How can I choose which of the 5 tasks will I kill? I can define each task by it's variable, but I can't access its CancellationRequested property since it is triggered with CancellationToken. Would I need 5 different tokens then?
None of the tasks get ever cancelled when I run the program.
That's because you're only ever checking the cancellation token at the start of the task. Once it's got past that first token.IsCancellationRequested check, cancelling the token does nothing. If you move your check into your loop, like this:
while (sw.ElapsedMilliseconds < time)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancelled");
return;
}
Thread.SpinWait(1000);
}
... then you'll see the tasks react appropriately.
Also what is bugging me is...what task am I really cancelling when calling token.Cancel()?
You're not cancelling a task - you're cancelling the cancellation token. Any task that observes that cancellation token will be cancelled (or complete, or whatever action it takes) but there's no direct correlation between a task and a token.
When we talk about "cancelling a task" we really mean "cancelling a token which we believe the task is observing".
TL;DR:
Is it possible to kick off a series of IO bound tasks using only one thread using async await?
In less brief:
Trying to learn async await. In this video ("Async Best Practices for C# and Visual Basic"), the speaker gives an example of using async await to kick off some IO bound work. He explicitly says (at 21m 40s) whilst explaining why parallel for loops are not optimal as they use up loads of threads:
We don't need more threads for this. We don't need two threads...
Can we really kick off multiple requests asyncronously without using more than one thread? How? Unfortunately, the speaker didn't provide all the code so here's my stab at it:
// Pretty much exactly the same as video
private async Task<List<string>> LoadHousesAsync()
{
// Running on the UI thread
Debug.Print("Thread: " + Thread.CurrentThread.ManagedThreadId);
var tasks = new List<Task<string>>();
for (int i = 0; i < 5; i++)
{
Task<string> t = LoadHouseAsync(i);
tasks.Add(t);
}
string[] loadedHouses = await Task.WhenAll(tasks);
return loadedHouses.ToList();
}
// My guess of the LoadHouseAsync method
private Task<string> LoadHouseAsync(int i)
{
// Running on the UI thread
Debug.Print("Thread: " + Thread.CurrentThread.ManagedThreadId);
return Task.Run(() => LoadHouse(i));
}
// My guess of the LoadHouse method
private string LoadHouse(int i)
{
// **** This is on a different thread :( ****
Debug.Print("Thread: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000); // simulate I/O bound work
return "House" + i;
}
Here's the output.
Thread: 10
Thread: 10
Thread: 3
Thread: 10
Thread: 10
Thread: 11
Thread: 10
Thread: 12
Thread: 10
Thread: 13
Thread: 14
You can do it with async I/O. What you made is a very nice example of doing it wrong (unfortunately, it's also quite common).
Task.Run runs a method on a thread pool thread, Thread.Sleep blocks the thread. So your example simulates doing synchronous (blocking) I/O on multiple threads.
To correctly perform async I/O, you need to use async methods all the way down. Never use Task.Run for I/O. You can simulate an asynchronous I/O method using Task.Delay:
private async Task<string> LoadHouseAsync(int i)
{
Debug.Print("Thread: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(5000); // simulate async I/O bound work
return "House" + i;
}
Can we really kick off multiple requests asyncronously without using more than one thread?
Yes, here is a real world example (pseudo asp.net-mvc and entity framework, where EF will make IO calls to Sql Server for example).
public async ActionResult()
{
var model = new Company();
using (var db1 = new DbContext)
using (var db2 = new DbContext)
{
var task1 = db1.Employees.ToListAsync();
var task2 = db1.Managers.ToListAsync();
await Task.WhenAll(task1, task2);
model.employees = task1.Result;
model.managers = task2.Result;
}
return View(model);
}
How can I check if a thread returned to the thread pool, using VS C# 2015 debugger?
What's problematic in my case is the fact that it cannot be detected by debugging line by line.
async Task foo()
{
int y = 0;
await Task.Delay(5);
// (1) thread 2000 returns to thread pool here...
while (y<5) y++;
}
async Task testAsync()
{
Task task = foo();
// (2) ... and here thread 2000 is back from the thread pool, to run the code below. I want
// to confirm that it was in the thread pool in the meantime, using debugger.
int i = 0;
while (i < 100)
{
Console.WriteLine("Async 1 before: " + i++);
}
await task;
}
In the first line of testAsync running on thread 2000, foo is called. Once it encounters await Task.Delay(5), thread 2000 returns to thread pool (allegedly, I'm trying to confirm this), and the method waits for Task.Delay(5) to complete. In the meantime, the control returns to the caller and the first loop of testAsync is executed on thread 2000 as well.
So between two consecutive lines of code, the thread returned to thread pool and came back from there. How can I confirm this with debugger? Possibly with Threads debugger window?
To clarify a bit more what I'm asking: foo is running on thread 2000. There are two possible scenarios:
When it hits await Task.Delay(5), thread 2000 returns to the thread pool for a very short time, and the control returns to the caller, at line (2), which will execute on thread 2000 taken from the thread pool. If this is true, you can't detect it easily, because Thread 2000 was in the thread pool during time between two consecutive lines of code.
When it hits await Task.Delay(5), thread 2000 doesn't return to thread pool, but immediately executes code in testAsync starting from line (2)
I'd like to verify which one is really happening.
There is a major mistake in your assumption:
When it hits await Task.Delay(5), thread 2000 returns to the thread pool
Since you don't await foo() yet, when thread 2000 hits Task.Delay(5) it just creates a new Task and returns to testAsync() (to int i = 0;). It moves on to the while block, and only then you await task. At this point, if task is not completed yet, and assuming the rest of the code is awaited, thread 2000 will return to the thread pool. Otherwise, if task is already completed, it will synchronously continue from foo() (at while (y<5) y++;).
EDIT:
what if the main method called testAsync?
When synchronous method calls and waits async method, it must block the thread if the async method returns uncompleted Task:
void Main()
{
var task = foo();
task.Wait(); //Will block the thread if foo() is not completed.
}
Note that in the above case the thread is not returning to the thread pool - it is completely suspended by the OS.
Maybe you can give an example of how to call testAsync so that thread 2000 returns to the thread pool?
Assuming thread 2k is the main thread, it cannot return to the thread pool. But you can use Task.Run(()=> foo()) to run foo() on the thread pool, and since the calling thread is the main thread, another thread pool thread will pick up that Task. So the following code:
static void Main(string[] args)
{
Console.WriteLine("main started on thread {0}", Thread.CurrentThread.ManagedThreadId);
var testAsyncTask = Task.Run(() => testAsync());
testAsyncTask.Wait();
}
static async Task testAsync()
{
Console.WriteLine("testAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
Console.WriteLine("testAsync continued on thread {0}", Thread.CurrentThread.ManagedThreadId);
}
Produced (on my PC) the following output:
main started on thread 1
testAsync started on thread 3
testAsync continued on thread 4
Press any key to continue . . .
Threads 3 and 4 came from and returned to the thread pool.
You can print out the Thread.CurrentThread.ManagedThreadId to the console. Note that the thread-pool is free to re-use that same thread to run continuations on it, so there's no guarantee that it'll be different:
void Main()
{
TestAsync().Wait();
}
public async Task FooAsync()
{
int y = 0;
await Task.Delay(5);
Console.WriteLine($"After awaiting in FooAsync:
{Thread.CurrentThread.ManagedThreadId }");
while (y < 5) y++;
}
public async Task TestAsync()
{
Console.WriteLine($"Before awaiting in TestAsync:
{Thread.CurrentThread.ManagedThreadId }");
Task task = foo();
int i = 0;
while (i < 100)
{
var x = i++;
}
await task;
Console.WriteLine($"After awaiting in TestAsync:
{Thread.CurrentThread.ManagedThreadId }");
}
Another thing you can check is ThreadPool.GetAvailableThreads to determine if another worker has been handed out for use:
async Task FooAsync()
{
int y = 0;
await Task.Delay(5);
Console.WriteLine("Thread-Pool threads after first await:");
int avaliableWorkers;
int avaliableIo;
ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);
Console.WriteLine($"Available Workers: { avaliableWorkers},
Available IO: { avaliableIo }");
while (y < 1000000000) y++;
}
async Task TestAsync()
{
int avaliableWorkers;
int avaliableIo;
ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);
Console.WriteLine("Thread-Pool threads before first await:");
Console.WriteLine($"Available Workers: { avaliableWorkers},
Available IO: { avaliableIo }");
Console.WriteLine("-------------------------------------------------------------");
Task task = FooAsync();
int i = 0;
while (i < 100)
{
var x = i++;
}
await task;
}
On my machine, this yields:
Thread-Pool threads before first await:
Available Workers: 1023, Available IO: 1000
----------------------------------------------
Thread-Pool threads after first await:
Available Workers: 1022, Available IO: 1000
I'd like to verify which one is really happening.
There is no way to "verify" that with debugger, because the debugger is made to simulate the logical (synchronous) flow - see Walkthrough: Using the Debugger with Async Methods.
In order to understand what is happening (FYI it's your case (2)), you need to learn how await works starting from Asynchronous Programming with Async and Await - What Happens in an Async Method section, Control Flow in Async Programs and many other sources.
Look at this snippet:
static void Main(string[] args)
{
Task.Run(() =>
{
// Initial thread pool thread
var t = testAsync();
t.Wait();
});
Console.ReadLine();
}
If we make the lambda to be async and use await t; instead of t.Wait();, this is the point where the initial thread will be returned to the thread pool. As I mentioned above, you cannot verify that with debugger. But look at the above code and think logically - we are blocking the initial thread, so if it' wasn't free, your testAsync and foo methods will not be able to resume. But they do, and this can easily be verified by putting breakpoint after await lines.
Shouldn't the await Subtask() call return control back to the Main function immediately?
When I run this program it actually waits till the long running double for loop(inside Subtask) to finish and only returns back to main after the following Task.Delay() is called.
Why is it not returning as soon as await Subtask() is called?
static void Main(string[] args)
{
Console.WriteLine("Main() threadId: " + Thread.CurrentThread.ManagedThreadId);
SuperTask();
Console.Read();
}
static async Task SuperTask()
{
Console.WriteLine("SuperTask threadId: " + Thread.CurrentThread.ManagedThreadId);
await SubTask();
}
static async Task SubTask()
{
Console.WriteLine("ThreadId for SubTask before await is: " + Thread.CurrentThread.ManagedThreadId);
var x = 0;
for (var i = 0; i < 25000; i++)
{
for (var j = 0; j < 250000; j++)
{
x -= i;
}
}
await Task.Delay(10000);
Console.WriteLine("ThreadId for SubTask is: " + Thread.CurrentThread.ManagedThreadId);
}
As I explain in my async intro, the async keyword does not start a new thread or anything crazy like that. Every async method begins executing synchronously.
This is by design, the Subtask() method returns as soon as an await statement is reached and the await Delay operation is kicked off. I explain a bit more here
You are probably experiencing some kind of race issue although Console should be immune to that. Check the marked answer to this question: Strange behaviour of Console.ReadKey() with multithreading
This is by design. await starts as a synchronous call until it finds another await in the call chain which actually result into a thread creation. In your scenario this is until call to Task.Delay which actually creates a thread (which is doing nothing and just waiting for 10 sec in this case) but the call can now return to the caller. In other case if you put your looping code in Task.Run for example, then control would return immediately to the caller