C# - async/await print order - c#

I am trying to understand how async and await works so i wrote this little program to try and print "b" before "a" but it's not working and i can't figure it out.. any help? :)
public class program
{
static async Task Main()
{
await a();
b();
}
async static Task a()
{
Console.WriteLine("Waiting for a");
for (int i = 0; i < 100000000; i++)
if (i % 1000000 == 0)
Console.Write(".");
Console.WriteLine("\na");
}
static void b()
{
Console.WriteLine("b");
}
}

Lets refine your code. First it doesn't work, because you told the code to await a(), and so nothing after that line can run before (or during) it. Lets fix it:
static async Task Main()
{
var task = a();
b();
await task;
}
However that is not enough, it still doesn't work. It still runs a() before b() because your a method is not asynchronous. In fact it is synchronous. Just because you use async/await it doesn't make the code automatically asynchronous. You'll need more than that. One way is to add await Task.Yield() which is truely asynchronous call, even though it does nothing (except for allowing the switch to happen). Like this:
async static Task a()
{
Console.WriteLine("Waiting for a");
for (int i = 0; i < 100000000; i++)
if (i % 1000000 == 0)
{
Console.Write(".");
await Task.Yield();
}
Console.WriteLine("\na");
}
Now you will see b() running in the middle of a(). You can play around with the await Task.Yield(); call, and put it in different places to see different result.
This is still a bit primitive example. It would be better to make b() asynchronous as well. And use delays. Like this:
public class program
{
static async Task Main()
{
var task1 = a();
var task2 = b();
await task1;
await task2;
}
async static Task a()
{
Console.WriteLine("Waiting for a");
for (var i = 0; i < 100; i++)
{
Console.Write(".");
await Task.Delay(20);
}
Console.WriteLine("\na");
}
static async Task b()
{
await Task.Delay(200);
Console.WriteLine("b");
}
}
Note that I've slightly modified your a() so that it is more time dependent, less cpu-speed dependent.
Of course you have a thread-safety issue now, which in this case is ok since Console and Task methods are thread-safe. But generally you have to be aware that when two or more Tasks run concurrently then they may run on different threads, and thus you have to make the code thread-safe.

There are 2 problems.
Await is being used incorrectly
You use await to ensure whichever async method is called is completed before any subsequent code is executed.
As freakish said in his answer, you can assign the task from method a to a variable and await it after method b.
Method a isn't actually asynchronous
There isn't any thing that can actually be awaited in the method so it would just run synchronously and complete before method b. Adding something that can be awaited like Task.Yeild (as freakish has stated) would resolve the issue.
I would recommend reading more about asynchronous programming, here is a good place to start: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Related

Why a simple await Task.Delay(1) enables parallel execution?

I would like to ask simple question about code bellow:
static void Main(string[] args)
{
MainAsync()
//.Wait();
.GetAwaiter().GetResult();
}
static async Task MainAsync()
{
Console.WriteLine("Hello World!");
Task<int> a = Calc(18000);
Task<int> b = Calc(18000);
Task<int> c = Calc(18000);
await a;
await b;
await c;
Console.WriteLine(a.Result);
}
static async Task<int> Calc(int a)
{
//await Task.Delay(1);
Console.WriteLine("Calc started");
int result = 0;
for (int k = 0; k < a; ++k)
{
for (int l = 0; l < a; ++l)
{
result += l;
}
}
return result;
}
This example runs Calc functions in synchronous way. When the line //await Task.Delay(1); will be uncommented, the Calc functions will be executed in a parallel way.
The question is: Why, by adding simple await, the Calc function is then async?
I know about async/await pair requirements. I'm asking about what it's really happening when simple await Delay is added at the beginning of a function. Whole Calc function is then recognized to be run in another thread, but why?
Edit 1:
When I added a thread checking to code:
static async Task<int> Calc(int a)
{
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
int result = 0;
for (int k = 0; k < a; ++k)
{
for (int l = 0; l < a; ++l)
{
result += l;
}
}
return result;
}
it is possible to see (in console) different thread id's. If await Delay line is deleted, the thread id is always the same for all runs of Calc function. In my opinion it proves that code after await is (can be) runned in different threads. And it is the reason why code is faster (in my opinion of course).
It's important to understand how async methods work.
First, they start running synchronously, on the same thread, just like every other method. Without that await Task.Delay(1) line, the compiler will have warned you that the method would be completely synchronous. The async keyword doesn't, by itself, make your method asynchronous. It just enables the use of await.
The magic happens at the first await that acts on an incomplete Task. At that point the method returns. It returns a Task that you can use to check when the rest of the method has completed.
So when you have await Task.Delay(1) there, the method returns at that line, allowing your MainAsync method to move to the next line and start the next call to Calc.
How the continuation of Calc runs (everything after await Task.Delay(1)) depends on if there is a "synchronization context". In ASP.NET (not Core) or a UI application, for example, the synchronization context controls how the continuations run and they would run one after the other. In a UI app, it would be on the same thread it started from. In ASP.NET, it may be a different thread, but still one after the other. So in either case, you would not see any parallelism.
However, because this is a console app, which does not have a synchronization context, the continuations happen on any ThreadPool thread as soon as the Task from Task.Delay(1) completes. That means each continuation can happen in parallel.
Also worth noting: starting with C# 7.1 you can make your Main method async, eliminating the need for your MainAsync method:
static async Task Main(string[] args)
An async function returns the incomplete task to the caller at its first incomplete await. After that the await on the calling side will await that task to become complete.
Without the await Task.Delay(1), Calc() does not have any awaits of its own, so will only return to the caller when it runs to the end. At this point the returned Task is already complete, so the await on the calling site immediately uses the result without actually invoking the async machinery.
layman's version....
nothing in the process is yielding CPU time back without 'delay' and so it doesn't give anything else CPU time, you are confusing this with multiple threaded code. "async and await" is not about multiple threaded but about using the CPU (thread/threads) when its doing non CPU work" aka writing to disk. Writing to disk does not need the thread (CPU). So when something is async, it can free the thread and be used for something else instead of waiting for non CPU (oi task) to complete.
#sunside is saying the same thing just more technically.
static async Task<int> Calc(int a)
{
//faking a asynchronous .... this will give this thread to something else
// until done then return here...
// does not make sense... as your making this take longer for no gain.
await Task.Delay(1);
Console.WriteLine("Calc started");
int result = 0;
for (int k = 0; k < a; ++k)
{
for (int l = 0; l < a; ++l)
{
result += l;
}
}
return result;
}
vs
static async Task<int> Calc(int a)
{
using (var reader = File.OpenText("Words.txt"))
{
//real asynchronous .... this will give this thread to something else
var fileText = await reader.ReadToEndAsync();
// Do something with fileText...
}
Console.WriteLine("Calc started");
int result = 0;
for (int k = 0; k < a; ++k)
{
for (int l = 0; l < a; ++l)
{
result += l;
}
}
return result;
}
the reason it looks like its in "parallel way" is that its just give the others tasks CPU time.
example; aka without delay
await a; do this (no actual aysnc work)
await b; do this (no actual aysnc work)
await c; do this (no actual aysnc work)
example 2;aka with delay
await a; start this then pause this (fake async), start b but come back and finish a
await b; start this then pause this (fake async), start c but come back and finish b
await c; start this then pause this (fake async), come back and finish c
what you should find is although more is started sooner, the overall time will be longer as it as to jump between tasks for no benefit with a faked asynchronous task. where as, if the await Task.Delay(1) was a real async function aka asynchronous in nature then the benefit would be it can start the other work using the thread which would of been blocked... while it waits for something which does not require the thread.
update silly code to show its slower... Make sure you are in "Release" mode you should always ignore the first run... these test are silly and you would need to use https://github.com/dotnet/BenchmarkDotNet to really see the diff
static void Main(string[] args)
{
Console.WriteLine("Exmaple1 - no Delay, expecting it to be faster, shorter times on average");
for (int i = 0; i < 10; i++)
{
Exmaple1().GetAwaiter().GetResult();
}
Console.WriteLine("Exmaple2- with Delay, expecting it to be slower,longer times on average");
for (int i = 0; i < 10; i++)
{
Exmaple2().GetAwaiter().GetResult();
}
}
static async Task Exmaple1()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task<int> a = Calc1(18000); await a;
Task<int> b = Calc1(18000); await b;
Task<int> c = Calc1(18000); await c;
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
}
static async Task<int> Calc1(int a)
{
int result = 0;
for (int k = 0; k < a; ++k) { for (int l = 0; l < a; ++l) { result += l; } }
return result;
}
static async Task Exmaple2()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task<int> a = Calc2(18000); await a;
Task<int> b = Calc2(18000); await b;
Task<int> c = Calc2(18000); await c;
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
}
static async Task<int> Calc2(int a)
{
await Task.Delay(1);
int result = 0;
for (int k = 0; k < a; ++k){for (int l = 0; l < a; ++l) { result += l; } }
return result;
}
By using an async/await pattern, you intend for your Calc method to run as a task:
Task<int> a = Calc(18000);
We need to establish that tasks are generally asynchronous in their nature, but not parallel - parallelism is a feature of threads. However, under the hood, some thread will be used to execute your tasks on. Depending on the context your running your code in, multiple tasks may or may not be executed in parallel or sequentially - but they will be (allowed to be) asynchronous.
One good way of internalizing this is picturing a teacher (the thread) answering question (the tasks) in class. A teacher will never be able to answer two different questions simultaneously - i.e. in parallel - but she will be able to answer questions of multiple students, and can also be interrupted with new questions in between.
Specifically, async/await is a cooperative multiprocessing feature (emphasis on "cooperative") where tasks only get to be scheduled onto a thread if that thread is free - and if some task is already running on that thread, it needs to manually give way. (Whether and how many threads are available for execution is, in turn, dependent on the environment your code is executing in.)
When running Task.Delay(1) the code announces that it is intending to sleep, which signals to the scheduler that another task may execute, thereby enabling asynchronicity. The way it's used in your code it is, in essence, a slightly worse version of Task.Yield (you can find more details about that in the comments below).
Generally speaking, whenever you await, the method currently being executed is "returned" from, marking the code after it as a continuation. This continuation will only be executed when the task scheduler selects the current task for execution again. This may happen if no other task is currently executing and waiting to be executed - e.g. because they all yielded or await something "long-running".
In your example, the Calc method yields due to the Task.Delay and execution returns to the Main method. This, in turn enters the next Calc method and the pattern repeats. As was established in other answers, these continuations may or may not execute on different threads, depending on the environment - without a synchronization context (e.g. in a console application), it may happen. To be clear, this is neither a feature of Task.Delay nor async/await, but of the configuration your code executes in. If you require parallelism, use proper threads or ensure that your tasks are started such that they encourage use of multiple threads.
In another note: Whenever you intend to run synchronous code in an asynchronous manner, use Task.Run() to execute it. This will make sure it doesn't get in your way too much by always using a background thread. This SO answer on LongRunning tasks might be insightful.

Why await an async function directly not work without assigning it to a Task variable

Typically, I do the following
public static async Task dosth()
{
List<Task> job = new List<Task>();
for (int i = 0; i < 3; i++)
{
job.Add(sleep());
}
Task.WhenAll(job.ToArray());
}
static async Task sleep()
{
await Task.Delay(1000);
Console.WriteLine("Finish new");
}
It works smoothly, no problem. But when I do a review on my own code (trying using other syntax to do the same job), I suddenly figure out the following two are different.
public static async Task dosthA()
{
//This will be working synchronously, take 3 seconds.
await sleep();
await sleep();
await sleep();
//This will be working asynchronously, take 1 second only.
Task A = sleep();
Task B = sleep();
Task C = sleep();
await A;
await B;
await C;
}
Why assigning the async function to a new variable make difference? I originally think they are the same.
Update
Why it is confusing me is, actually in Microsoft doc on Async-await,
They stated the following in their code.
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
They are actually different, why they use //or , in a single statement, just because it makes no different in their own example?
This is because when you are returning a running Task when you call Sleep() even when you're assigning to a variable.
The confusion is that the Task does not begin if you assign it to a variable (A, B, or C) until you call await A; but that's not true. As soon as you assign sleep(); to A, sleep() was called; therefore the Task in the sleep() method is running. Assigning it to a variable or not the Task begins when you call the method; because in the method you start the Task.
Knowing this; when you call:
await A;
await B;
await C;
A, B, and C, have already starting simultaneously... After awaiting A it is most likely B, and C have also completed or are milliseconds from completing.
There are situations where you can reference a Task that hasn't started yet but you would have to purposely return a non-running Task to do that.
To answer the edit to your question also.
Tasks have a method called GetAwaiter() which returns a TaskAwaiter. In C# when you write var task = sleep(); then you're assigning the actual Task to the task variable. All the same when you write await sleep(); the compiler does some cool stuff and it actually calls the Task.GetAwaiter() method; which is subscribed to. The Task will run and when it is complete the TaskAwaiter fires the continuation action. This can't be explained in a simple answer but to know the outer logic helps.
Among other things the TaskAwaiter implements ICriticalNotifyCompletion which in turn implements INotifyCompletion. Both have one method each, OnCompleted(Action) and UnsafeOnCompleted(Action) (you can guess which is which by naming convention).
Another thing to note is that Task.GetAwaiter() returns a TaskAwaiter but Task<TResult>.GetAwaiter() returns a TaskAwaiter<TResult>. There's not a strong difference in the two but there is a difference in the GetResult() method of the two tasks; which is what's called while marshalling back to the proper threading context. The TaskAwaiter.GetResult() returns void and the TaskAwaiter<TResult>.GetResult() returns TResult.
I feel like if I push further into this I'll have to write pages to explain it all in detail... Hopefully just explaining your question and pulling the curtain back a little bit will shed enough light to help you both understand and dig deeper if you're more curious.
Ok, so based on the comment below I want to describe my answer a little bit further.
I'll start this simple; let's just make a Task; one that isn't running, and look at it first.
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
We can now call code like:
public async void DoWork()
{
await GetTask();
}
… but we'll be waiting forever; until the application ends, because the Task was never started. However; we could do something like this:
public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
… and it will await the running Task and continue once the Task is complete.
Knowing this you can make as many calls to GetTask() as you like and you'll only be referencing Tasks that have not started.
In your code it's just the opposite, which is fine, as this is the most used way. I encourage you to make sure your method names notify the user of how you're returning the Task. If the Task is already running the most common convention is the end the method name with Async. Here's another example doing it with a running Task for clarity.
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
And now we will most likely call this method like:
public async void DoWork()
{
await DoTaskAsync();
}
However; note that if we simply want to reference the Task just like we did earlier, we can, the only difference is this Task is running where the one prior was not. So this code is valid.
public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
The big take away is how C# handles the async / await keywords. async tells the compiler that the method is going to become a continuation of a Task. In short; the compiler knows to look for all await calls and put the rest of the method in a continuation.
The await keyword tells the compiler to call the Task.GetAwaiter() method on the Task ( and basically subscribe to the INotifyCompletion and ICriticalNotifyCompletion) to signal the continuation in the method.
And this I wanted to add just incase you weren't aware. If you do have more than one task that you want to await but would rather await one task as if they were all one then you can do that with Task.WhenAll() So instead of:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
You could write it a little cleaner like so:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
And there are more ways of doing this sort of thing built in; just explore it.

Confusion on async and await logic in c#?

i have a great confusion while working with the async and await.
My logic is simple, i have a method which will cause a delay of 15 seconds, like this
public static Task<int> delay(int num)
{
Task.Delay(15000);
return Task.FromResult(num);
}
now i am calling this method like this
public static async void delayAsync(int num)
{
Console.WriteLine(await delay(num)+"time :"+DateTime.Now);
}
static void Main(string[] args)
{
var details = new details();
Console.WriteLine("## start ##");
for (var i = 0; i < 5; i++)
{
Console.WriteLine("counter: "+i);
delayAsync(i);
}
Console.WriteLine("## finished ##");
Console.Read();
}
my desired output is to get the number one by one after the delay of 15 seconds, but i am getting the results all at once without any pause. am i missing something.
I have gone through this blog and couldnt understand a thing
Here are the steps that you need to achieve this:
Await Task.Delay and return num instead of Task.
public static async Task<int> delay(int num)
{
await Task.Delay(15000);
return num;
}
Make your delayAsync method return Task.
public static async Task delayAsync(int num)
{
Console.WriteLine(await delay(num) + "time :" + DateTime.Now);
}
Wait for the result of the delayAsync in you Main method.
public static void Main(string[] args)
{
Console.WriteLine("## start ##");
for (var i = 0; i < 5; i++)
{
Console.WriteLine("counter: " + i);
delayAsync(i).Wait();
}
Console.WriteLine("## finished ##");
Console.Read();
}
You are not awaiting the Task.Delay
public static async Task<int> delay(int num){
await Task.Delay(15000);
return num;
}
Note that the signature of the method needs to include async. Additionally since you are now using await inside the method, you don't need to explicitly create a Task for the return statement.
Additionally you have to await the delayAsync call in your Main method (note that you cannot make Main async so you have to use a workaround like this: https://stackoverflow.com/a/9212343/868361
my desired output is to get the number one by one after the delay of 15 seconds
When you call an async method that you don't await then the compiler will start it and continue with the rest of the procedures. In your case with the rest of the loop. So the simple solution to get your desired output would be here:
Don't use async methods! Simply make it a serial processing.
To achieve that what you want using async you would have to wait in the loop for the delayAsync method. But this would make the asynchronous processing synchronous and you would end up again in the normal 1 thread serial processing, only having invested a lot of effort to end up from where you started.

async await and Task in C#

I'm testing the async await and task functionality but it seems i'm missing something
When I write like this A being starting point.
void A()
{
Debug.WriteLine("pre B");
B();
Debug.WriteLine("post B");
}
async void B()
{
Debug.WriteLine("pre C");
await C();
Debug.WriteLine("post C")
}
async Task C()
{
await Task.Yield();
Debug.WriteLine("pre D");
await D();
Debug.WriteLine("post D");
}
async Task D()
{
Debug.WriteLine("inside D");
}
I get output in debug console as
pre B,
pre C,
pre D,
inside D,
post D,
post C,
post B
Is it running asynchronously?? I guess not, what thought it should run like
pre B,
pre C,
post B,
post C,
pre D,
inside D,
post D
So what's the problem here?
I recommend you read my async intro and follow up with my async best practices article. In particular, one of the best practices is to only use async void for event handlers.
The whole point of async is to enable asynchronous code while keeping that code written in a similar way to synchronous code. So if an outer method awaits the task returned from an inner method, then the outer method will not continue executing until the inner method completes.
So, for example, synchronous code may look like this:
void A()
{
Debug.WriteLine("pre B");
B();
Debug.WriteLine("post B");
}
void B()
{
Debug.WriteLine("inside B");
Thread.Sleep(1000);
Debug.WriteLine("still inside B");
}
The corresponding asynchronous code would be:
async Task A()
{
Debug.WriteLine("pre B");
await B();
Debug.WriteLine("post B");
}
async Task B()
{
Debug.WriteLine("inside B");
await Task.Delay(1000);
Debug.WriteLine("still inside B");
}
Even though the code will now execute asynchronously, the output is exactly the same as the synchronous version.
If you want one method to start concurrent operations and then continue, you can just call the methods and then do the await later:
async Task A()
{
// Start both tasks
Debug.WriteLine("pre B1");
Task b1 = B();
Debug.WriteLine("post B1, pre B2");
Task b2 = B();
Debug.WriteLine("post B2, pre await");
// (asynchronously) wait for them to complete
await Task.WhenAll(b1, b2);
Debug.WriteLine("post await");
}
async Task B()
{
Debug.WriteLine("inside B");
await Task.Delay(1000);
Debug.WriteLine("still inside B");
}
And in this case, you'll see both executions of B start, and A will continue executing after both Bs complete.
I changed the code now it looks like
using System;
using System.Threading.Tasks;
namespace TaskClass
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Pre A");
A();
Console.WriteLine("Post A");
Console.ReadKey();
}
static async Task A()
{
Console.WriteLine("pre B");
await B();
Console.WriteLine("post B");
}
static async Task B()
{
Console.WriteLine("inside B");
await Task.Delay(10000);
Console.WriteLine("still inside B");
}
}
}
And output i'm getting is as suspected.
Pre A
pre B
inside B
Post A
still inside B
post B
So you see as soon as I'm hitting await Task.Delay(10000); the control goes back to Main method, as evident from the fact that its printing Post A before still inside B also after Console.WriteLine("Post A"); It waits for Console.ReadKey(); only because as soon as I hit any key execution stops. It doesn't wait for Console.WriteLine("still inside B");.
So simple yet so strange. .net!! :)
I guess, that it runs sync cuz you`re using await statements, which waits for your methods to be done.
So if you want to get result like 2d variant - try not to use await
That's normal. Async/Await means that the method is suspended until the task is complete. That task runs asynchronously but the current thread waits for it and the current thread is no blocked.
This is specially useful if you don't want to block the ui thread when a method waits for a result that may be long to come.
From MSDN
Async Improves Responsiveness
Asynchrony is essential for activities that are potentially blocking,
such as when your application accesses the web. Access to a web
resource sometimes is slow or delayed. If such an activity is blocked
within a synchronous process, the entire application must wait. In an
asynchronous process, the application can continue with other work
that doesn't depend on the web resource until the potentially blocking
task finishes.
The following table shows typical areas where asynchronous programming
improves responsiveness. The listed APIs from the .NET Framework 4.5
and the Windows Runtime contain methods that support async
programming.
Application area
Supporting APIs that contain async methods
Web access
HttpClient , SyndicationClient
Working with files
StorageFile, StreamWriter, StreamReader, XmlReader
Working with images
MediaCapture, BitmapEncoder, BitmapDecoder
WCF programming
Synchronous and Asynchronous Operations
Asynchrony proves especially valuable for applications that access the
UI thread because all UI-related activity usually shares one thread.
If any process is blocked in a synchronous application, all are
blocked. Your application stops responding, and you might conclude
that it has failed when instead it's just waiting
For example, you can have a method that returns a value.
public async Task<int> LongComputation()
And then, in your code you have:
var result = await LongComputation();
The current thread is not blocked. When the result is available, you get it in your variable and you can continue with it in your current thread.
If you want to launch a new task asynchronously, you can do it like that
Task.Factory.StartNew(async () =>
{
Debug.WriteLine("async start");
await Task.Delay(5000);
Debug.WriteLine("async end");
});
In your method B, you can try the following
async void B()
{
Debug.WriteLine("pre C");
var taskC = C();
Debug.WriteLine("post C")
await taskC;
}

How do I convert this to an async task?

Given the following code...
static void DoSomething(int id) {
Thread.Sleep(50);
Console.WriteLine(#"DidSomething({0})", id);
}
I know I can convert this to an async task as follows...
static async Task DoSomethingAsync(int id) {
await Task.Delay(50);
Console.WriteLine(#"DidSomethingAsync({0})", id);
}
And that by doing so if I am calling multiple times (Task.WhenAll) everything will be faster and more efficient than perhaps using Parallel.Foreach or even calling from within a loop.
But for a minute, lets pretend that Task.Delay() does not exist and I actually have to use Thread.Sleep(); I know in reality this is not the case, but this is concept code and where the Delay/Sleep is would normally be an IO operation where there is no async option (such as early EF).
I have tried the following...
static async Task DoSomethingAsync2(int id) {
await Task.Run(() => {
Thread.Sleep(50);
Console.WriteLine(#"DidSomethingAsync({0})", id);
});
}
But, though it runs without error, according to Lucien Wischik this is in fact bad practice as it is merely spinning up threads from the pool to complete each task (it is also slower using the following console application - if you swap between DoSomethingAsync and DoSomethingAsync2 call you can see a significant difference in the time that it takes to complete)...
static void Main(string[] args) {
MainAsync(args).Wait();
}
static async Task MainAsync(String[] args) {
List<Task> tasks = new List<Task>();
for (int i = 1; i <= 1000; i++)
tasks.Add(DoSomethingAsync2(i)); // Can replace with any version
await Task.WhenAll(tasks);
}
I then tried the following...
static async Task DoSomethingAsync3(int id) {
await new Task(() => {
Thread.Sleep(50);
Console.WriteLine(#"DidSomethingAsync({0})", id);
});
}
Transplanting this in place of the original DoSomethingAsync, the test never completes and nothing is shown on screen!
I have also tried multiple other variations that either do not compile or do not complete!
So, given the constraint that you cannot call any existing asynchronous methods and must complete both the Thread.Sleep and the Console.WriteLine in an asynchronous task, how do you do it in a manner that is as efficient as the original code?
The objective here for those of you who are interested is to give me a better understanding of how to create my own async methods where I am not calling anybody elses. Despite many searches, this seems to be the one area where examples are really lacking - whilst there are many thousands of examples of calling async methods that call other async methods in turn I cannot find any that convert an existing void method to an async task where there is no call to a further async task other than those that use the Task.Run(() => {} ) method.
There are two kinds of tasks: those that execute code (e.g., Task.Run and friends), and those that respond to some external event (e.g., TaskCompletionSource<T> and friends).
What you're looking for is TaskCompletionSource<T>. There are various "shorthand" forms for common situations so you don't always have to use TaskCompletionSource<T> directly. For example, Task.FromResult or TaskFactory.FromAsync. FromAsync is most commonly used if you have an existing *Begin/*End implementation of your I/O; otherwise, you can use TaskCompletionSource<T> directly.
For more information, see the "I/O-bound Tasks" section of Implementing the Task-based Asynchronous Pattern.
The Task constructor is (unfortunately) a holdover from Task-based parallelism, and should not be used in asynchronous code. It can only be used to create a code-based task, not an external event task.
So, given the constraint that you cannot call any existing asynchronous methods and must complete both the Thread.Sleep and the Console.WriteLine in an asynchronous task, how do you do it in a manner that is as efficient as the original code?
I would use a timer of some kind and have it complete a TaskCompletionSource<T> when the timer fires. I'm almost positive that's what the actual Task.Delay implementation does anyway.
So, given the constraint that you cannot call any existing
asynchronous methods and must complete both the Thread.Sleep and the
Console.WriteLine in an asynchronous task, how do you do it in a
manner that is as efficient as the original code?
IMO, this is a very synthetic constraint that you really need to stick with Thread.Sleep. Under this constraint, you still can slightly improve your Thread.Sleep-based code. Instead of this:
static async Task DoSomethingAsync2(int id) {
await Task.Run(() => {
Thread.Sleep(50);
Console.WriteLine(#"DidSomethingAsync({0})", id);
});
}
You could do this:
static Task DoSomethingAsync2(int id) {
return Task.Run(() => {
Thread.Sleep(50);
Console.WriteLine(#"DidSomethingAsync({0})", id);
});
}
This way, you'd avoid an overhead of the compiler-generated state machine class. There is a subtle difference between these two code fragments, in how exceptions are propagated.
Anyhow, this is not where the bottleneck of the slowdown is.
(it is also slower using the following console application - if you
swap between DoSomethingAsync and DoSomethingAsync2 call you can see a
significant difference in the time that it takes to complete)
Let's look one more time at your main loop code:
static async Task MainAsync(String[] args) {
List<Task> tasks = new List<Task>();
for (int i = 1; i <= 1000; i++)
tasks.Add(DoSomethingAsync2(i)); // Can replace with any version
await Task.WhenAll(tasks);
}
Technically, it requests 1000 tasks to be run in parallel, each supposedly to run on its own thread. In an ideal universe, you'd expect to execute Thread.Sleep(50) 1000 times in parallel and complete the whole thing in about 50ms.
However, this request is never satisfied by the TPL's default task scheduler, for a good reason: thread is a precious and expensive resource. Moreover, the actual number of concurrent operations is limited to the number of CPUs/cores. So in reality, with the default size of ThreadPool, I'm getting 21 pool threads (at peak) serving this operation in parallel. That is why DoSomethingAsync2 / Thread.Sleep takes so much longer than DoSomethingAsync / Task.Delay. DoSomethingAsync doesn't block a pool thread, it only requests one upon the completion of the time-out. Thus, more DoSomethingAsync tasks can actually run in parallel, than DoSomethingAsync2 those.
The test (a console app):
// https://stackoverflow.com/q/21800450/1768303
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Console_21800450
{
public class Program
{
static async Task DoSomethingAsync(int id)
{
await Task.Delay(50);
UpdateMaxThreads();
Console.WriteLine(#"DidSomethingAsync({0})", id);
}
static async Task DoSomethingAsync2(int id)
{
await Task.Run(() =>
{
Thread.Sleep(50);
UpdateMaxThreads();
Console.WriteLine(#"DidSomethingAsync2({0})", id);
});
}
static async Task MainAsync(Func<int, Task> tester)
{
List<Task> tasks = new List<Task>();
for (int i = 1; i <= 1000; i++)
tasks.Add(tester(i)); // Can replace with any version
await Task.WhenAll(tasks);
}
volatile static int s_maxThreads = 0;
static void UpdateMaxThreads()
{
var threads = Process.GetCurrentProcess().Threads.Count;
// not using locks for simplicity
if (s_maxThreads < threads)
s_maxThreads = threads;
}
static void TestAsync(Func<int, Task> tester)
{
s_maxThreads = 0;
var stopwatch = new Stopwatch();
stopwatch.Start();
MainAsync(tester).Wait();
Console.WriteLine(
"time, ms: " + stopwatch.ElapsedMilliseconds +
", threads at peak: " + s_maxThreads);
}
static void Main()
{
Console.WriteLine("Press enter to test with Task.Delay ...");
Console.ReadLine();
TestAsync(DoSomethingAsync);
Console.ReadLine();
Console.WriteLine("Press enter to test with Thread.Sleep ...");
Console.ReadLine();
TestAsync(DoSomethingAsync2);
Console.ReadLine();
}
}
}
Output:
Press enter to test with Task.Delay ...
...
time, ms: 1077, threads at peak: 13
Press enter to test with Thread.Sleep ...
...
time, ms: 8684, threads at peak: 21
Is it possible to improve the timing figure for the Thread.Sleep-based DoSomethingAsync2? The only way I can think of is to use TaskCreationOptions.LongRunning with Task.Factory.StartNew:
You should think twice before doing this in any real-life application:
static async Task DoSomethingAsync2(int id)
{
await Task.Factory.StartNew(() =>
{
Thread.Sleep(50);
UpdateMaxThreads();
Console.WriteLine(#"DidSomethingAsync2({0})", id);
}, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
}
// ...
static void Main()
{
Console.WriteLine("Press enter to test with Task.Delay ...");
Console.ReadLine();
TestAsync(DoSomethingAsync);
Console.ReadLine();
Console.WriteLine("Press enter to test with Thread.Sleep ...");
Console.ReadLine();
TestAsync(DoSomethingAsync2);
Console.ReadLine();
}
Output:
Press enter to test with Thread.Sleep ...
...
time, ms: 3600, threads at peak: 163
The timing gets better, but the price for this is high. This code asks the task scheduler to create a new thread for each new task. Do not expect this thread to come from the pool:
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Thread pool: " +
Thread.CurrentThread.IsThreadPoolThread); // false!
}, TaskCreationOptions.LongRunning).Wait();

Categories

Resources