I am trying to understand the code I wrote,
for (int i = 0; i< 5; i++)
{
ExecuteCMD("/c robocopy C:\\Source D:\\Source /MIR", true);
}
public async void ExecuteCMD(string cmdLine, bool waitForExit)
{
await Task.Factory.StartNew(() =>
{
ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
proc.Execute();
} );
}
The async method ExecuteCMD will run in a loop for 5 times. I know async doesn't create new threads. So, are there 5 objects created with the same name ('proc') in the same thread? Please explain
Many thanks in advance!
do you mean your ExecuteProcess proc Object? This is a local variable of your lambda function. So there is no conflict in your code.
The Lambda
() =>
{
ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
proc.Execute();
}
is called 5 Times but every call creates only one instance of ExecuteProcess for the variable proc.
You are using Task.Factory.StartNew, so you will most likely (see Stephen Cleary's comment) end up on the default TaskScheduler, which happens to execute work on thread pool threads. Your ExecuteProcess allocation and Execute call will therefore occur 5 times, as expected, on thread pool threads (see above point re default scheduler) - and most likely in parallel to each other, and in parallel to your for loop (this last part might be difficult to wrap your head around, but that's the whole problem with async void - the execution order is non-deterministic; more on that later).
You are sort of right in that async/await does not necessarily create new threads. async/await is all about chaining tasks and their continuations so that they execute in correct order with respect to each other. Where the actual Task runs is determined by how that Task is created. Here you are explicitly requesting your work to be pushed out to the thread pool, because that is where Tasks created by Task.Factory.StartNew execute.
Others have pointed out that you might be using async void erroneously - possibly due to lack of understanding. async void is only good for scheduling work in a fire-and-forget manner, and this work needs to be self-contained, complete with its own exception handling and concurrency controls. Because your async void will run unobserved, in parallel to the rest of your code. It's like saying: "I want this piece of code to run at some point in the future. I don't need to know when it completes or whether it raised any exceptions - I'll just wait until it hits the first await, and then carry on executing my own work - and the rest of the async void will proceed on its own, in parallel, without supervision". Because of this, if you put some code after your for loop, it will most likely execute before your ExecuteProcess work, which may or may not be what you want.
Here's a step-by-step view of what actually happens in your application.
You hit the first iteration of the for loop on the main thread. The runtime calls ExecuteCMD, as if it were any other, synchronous method call. It enters the method and executes the code preceding the await, still as part of the first for loop iteration, on the main thread. Then it schedules some work on the thread pool via Task.Factory.StartNew. This work will execute at some point in the future on, let's say, thread pool thread #1. The Task returned by Task.Factory.StartNew is then awaited. This task cannot possibly complete synchronously, so the await schedules the rest of your async void to run in the future (on the main thread, after the task created by Task.Factory.StartNew has completed) and yields. At this point your ExecuteProcess work probably hasn't even started yet, but the main thread is already free to jump to the second iteration of the for loop. It does exactly that, which results in another task being scheduled to run on, say, thread pool thread #2, at some point in the future - followed by yet another continuation scheduled to run on the main thread. The for loop then jumps to the next item.
By the time your for loop ends, you will most likely have 5 Tasks waiting to execute on thread pool threads, followed by 5 continuation Tasks waiting to execute on the main thread. They will all complete at some point in the future. Your code won't know when because you told it that you don't care (by using async void).
Related
I'm trying to wrap my head around control flow in C# when using async, Task, and await.
I understand how promises work, and that the returned Task<> from an async method will eventually contain the result of a computation/IO/whatever.
I think I understand that if you explicitly wait for that Task, then the current thread blocks until the Task is complete. I also think that means that the code in the async method that returns a Task will be running on a thread in a thread pool.
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
Here's an example. Here's I'm using UniTask which is basically Tasks for Unity:
public async UniTask ConnectAsync(Connection connection)
{
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
// Close Any Old Connections
await DisconnectAsync();
// Default Address
if (string.IsNullOrEmpty(connection.Address)) { connection.Address = "localhost:6379"; }
// Connect
ConfigurationOptions config = new()
{
EndPoints =
{
{ connection.Address, connection.Port },
},
User = connection.Username,
Password = connection.Password,
};
m_Connection = await ConnectionMultiplexer.ConnectAsync(config);
// Create Graph Client
m_Graph = new(m_Connection.GetDatabase());
// Notify
await Editor.Controller.OnConnect();
Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
}
If I call this method, and then neglect to await the returned Task (UniTask), both Debug.Log() show that execution is happening on the "Main Thread" (i.e. the UI thread).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state? I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
EDIT: I believe the chosen answer basically answered the question in the final sentence:
"Your code just needs to return to the main loop to allow the
continuation to run."
In other words, there's a loop somewhere deep in the bowels of (Unity in this case) and if the UI thread gets there, then it takes the opportunity to continue any pending tasks. (Please correct me in a comment if this is wrong and I'll update accordingly).
Incidentally, these links were very informative:
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
https://www.ncameron.org/blog/async-io-fundamentals/
https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/
https://blog.stephencleary.com/2012/02/async-and-await.html
What I don't understand is what happens if I don't "await" the Task returned by an asynchronous method. It seems to me that the continuation is executed on the original thread that calls the async method, but I have no idea how control can return to that thread.
As I describe on my blog, each await (by default) captures a "context", which is SynchronizationContext.Current or TaskScheduler.Current. In this particular case, the UI context is captured and used to resume the async method (i.e., execute the continuation).
How is it that without awaiting this Task, the Main Thread is able to return to this continuation? Does C# wait until the thread is in the Suspended/WaitSleepJoin state?
It has to do with contexts, not threads. The UI context schedules work by posting to the main UI message queue. So the continuation is run when the UI thread processes its message queue; it doesn't have anything to do with thread states.
I'm not aware of any code putting the UI thread to sleep so I'm not sure about that. I'm certainly not putting the UI to sleep.
Your code just needs to return to the main loop to allow the continuation to run.
I understand how promises work
Good, then we can stop right there. Tasks are nothing but compiler syntactic sugar over a promise. In fact, when JavaScript copied the await/async keywords from C#, they got implemented over the native Promise object.
Now, for the remainder of this I'm going to assume that you don't know how promises work. Think of it as getting called out on your promise bluff on your CV.
There's three parts to an async method:
The "synchronous" part. This is what will run when you simply call your async function, awaiting it or not, and is everything before the first await in your function. In your function this is the Debug.Log call and the synchronous part of DisconnectAsync.
The "asynchronous" part, the tail of your function. This gets stored as a lambda and it captures all necessary variables on creation. This gets called after #1 and when "done" it returns the Task object from your function. When the task is fully completed, the task is set as completed. Note that this can be recursive if you have multiple tails inside your tail.
All the magic of Task. For example, Task.WhenAll instantiates mutexes in your Task and then waits on them for completion. This makes Task technically disposable, and thus a memory and OS handle leak if you don't dispose every single task you create. await itself is handled through TaskCompletionSource, and you get just the task it manages. Things like that.
Note that nowhere in this did I mention threads. Tasks are to threads like what cats are to doctors. They both exist, some interact, but you have to be pretty insane to say cats are made to work only with doctors. Instead, tasks work on contexts. Thread pools are one default context. Another is single threaded contexts.
That's right, you can easily have async code run on a single thread, which is perfect for GUI in a single threaded render loop-driven game. You create a dialog, await its showing and get a result, all without any additional threads. You start an animation and await its completion, all without any threads.
I'm having a piece of code that I'm not quite sure if this would run asynchronously. Below I've made up some sample scripts which truly reflects the situation. Please note that the GetAsync methods are proper asyn methods having async/await keywords and return type using the Task of the related object.
public async Task<SomeResults> MyMethod()
{
var customers = _customerApi.GetAllAsync("some_url");
var orders = _orderApi.GetAllAsync("some_url");
var products = _productApi.GetAllAsync("some_url");
await Task.WhenAll(customers, orders, products);
// some more processing and returning the results
}
Question 1: Would the three above API calls run asynchronously even though there's no await before them? But, we have the await before Task.WhenAll?
Question 2: Would the above code run asynchronously if the await keyword is removed from before the Task.WhenAll?
I've tried to Google it around but couldn't find the proper answer to this specific situation. I've started reading Parallel Programming in Microsoft .NET but yet have long way to finish it so I couldn't just wait it.
Question 1: Would the three above API calls run asynchronously even though there's no await before them? But, we have the await before Task.WhenAll?
If the methods are actually doing something asynchronously, then yes.
Question 2: Would the above code run asynchronously if the await keyword is removed from before the Task.WhenAll?
If the methods are actually doing something asynchronously, then yes. However, it would be pointless to use Task.WhenAll without await.
Why I say "if": The async keyword doesn't magically make a method asynchronous, neither does the await operator. The methods still have to actually do something asynchronously. They do that by returning an incomplete Task.
All async methods start out running synchronously, just like any other method. The magic happens at await. If await is given an incomplete Task, then the method returns its own incomplete Task, with the rest of the method signed up as a continuation of that Task. That happens all the way up the call stack as long as you're using await all the way up the call stack.
Once the Task completes, then the continuation runs (the rest of the methods after await).
But at the top of the call stack needs to be something that's actually asynchronous. If you have an async method that calls an async method that calls a synchronous method, then nothing will actually run asynchronously, even if you use await.
For example, this will run completely synchronously (i.e. the thread will block) because an incomplete Task is never returned anywhere:
async Task Method1() {
await Method2();
}
async Task Method2() {
await Method3();
}
Task Method3() {
Thread.Sleep(2000);
return Task.CompletedTask;
}
However, this will run asynchronously (i.e. during the delay, the thread is freed to do other work):
async Task Method1() {
await Method2();
}
async Task Method2() {
await Method3();
}
async Task Method3() {
await Task.Delay(2000);
}
The key is in what Task.Delay returns. If you look at that source code, you can see it returns a DelayPromise (which inherits from Task), immediately (before the time is up). Since it's awaited, that triggers Method3 to return an incomplete Task. Since Method2 awaits that, it returns an incomplete Task, etc. all the way up the call stack.
YES, to both questions with a lot of caveats.
await / async is just syntactical sugar that allows you to write async code in a synchronous way. It doesn't magically spin up threads to make things run in parallel. It just allows the currently executing thread to be freed up to do other chunks of work.
Think of the await keyword as a pair of scissors that snips the current chunk of work into two, meaning the current thread can go and do another chunk while waiting for the result.
In order to do these chunks of work, there needs to be some kind of TaskScheduler. WinForms and WPF both provide TaskSchedulers that allow a single thread to process chunks one by one, but you can also use the default scheduler (via Task.Run()) which will use the thread pool, meaning lots of threads will run lots of chunks at once.
Assuming you are using a single thread, you example code would run as follows:
_customerApi.GetAllAsync() would run until it either completes, or hits an await. At that point it would return to a Task to your calling function which gets stuffed into customers.
_orderApi.GetAllAsync() would then run in exactly the same way. A Task will be assigned to orders which may or may not be complete.
ditto _productApi.GetAllAsync()
then you thread hits await Task.WhenAll(customers, orders, products); this means it can go and do other things, so the TaskScheduler might give it some other chunks of work to do, such as continuing to do the next bit of _customerApi.GetAllAsync().
Eventually all the chunks of work will be done, and your three tasks inside customers, orders, and products will be complete. At this point the scheduler knows that it can run the bit after WhenAll()
So you can see that in this case a SINGLE thread has run all the code, but not necessarily synchronously.
Whether your code runs asynchronously depends on your definition of asynchronous. If you look really close what happens, there will only be one thread that will do the stuff. However this thread won't be waiting idly as long as it has something to do.
A thing that really helped me to understand async-await was the cook-making-breakfast analogy in this interview with Eric Lippert. Search somewhere in the middle for async await.
Suppose a cook has to make breakfast. He starts boiling water for the tea. Instead of waiting idly for the water to cook, he inserts bread in the toaster. Not waiting idly again he starts boiling water for the eggs. As soon as the tea water boils he makes the tea and waits for the toast or the eggs.
Async-await is similar. Whenever your thread would have to wait for another process to finish, like a file to be written, a database query to return data, internet data to load, the thread won't be waiting idly for the other process to finish, but it will go up the call stack to see if any of the callers isn't awaiting and starts executing statements until it sees an await. Go up the call stack again and execute until the await. etc.
Because GetAllAsync is declared async, you can be certain that there is an await in it. In fact, your compiler will warn you if you declare a function async without an await in it.
Your thread will go into _customerApi.GetAllAsync("some_url"); and executes statements until is sees an await. If the task that your thread is awaiting for isn't complete, the thread goes up the call stack (your procedure) and starts executing the next statement:_orderApi.GetAllAsync("some_url"). It executes statements until is sees an await. Your function gets control again and calls the next method.
This goes on until your procedure starts awaiting. In this case, the awaitable method Task.WhenAll (not to be confused with the non-awaitable Task.WaitAll).
Even now, the thread won't be waiting idly, it will go up the call stack and execute statements until is meets an await, goes up the call stack again, etc.
So note: no new threads won't be started. While your thread is busy executing statements of the first method call, no statements of the second call will be executed, and while statements of the second call are being executed, no statements of the first call will be executed, not even if the first await is ready.
This is similar to the one and only cook: while he is inserting bread in the toaster, he can't process the boiling water for the tea: only after the bread is inserted and he would start waiting idly for it to be toasted he can continue making tea.
The await Task.WhenAll is not different to other awaits, except that the task is completed when all tasks are completed. So as long as any of the tasks is not ready, your thread won't execute statements after the WhenAll. However: your thread won't be waiting idly, it will go up the call stack and start executing statements.
So although it seems that two pieces of code are executed at the same time, it is not. If you really want two pieces of code to run simultaneously you'll have to hire a new cook using `Task.Run( () => SliceTomatoes);
Hiring a new cook (starting a new thread) is only meaningful if the other task is not async and your thread has other meaningful things to do, like keeping the UI responsive. Normally your cook would Slice the Tomatoes himself. Let your caller decide whether he hires a new cook (=you) to make the breakfast and slice the tomatoes.
I oversimplified it a bit, by telling you that there is only one thread (cook) involved. In fact, it can be any thread that continues executing the statements after your await. You can see that in the debugger by examining the thread ID, quite often it will be a different thread that will continue. However this thread has the same context as your original thread, so for you it will be as if it is the same thread: no need for a mutex, no need for IsInvokeRequired for user interface threads. More information about this can be found in articles from Stephen Cleary
I've searched the web and seen a lot of questions regarding Task.Run vs await async, but there is this specific usage scenario where I don't not really understand the difference. Scenario is quite simple i believe.
await Task.Run(() => LongProcess());
vs
await LongProcess());
where LongProcess is a async method with a few asynchronous calls in it like calling db with await ExecuteReaderAsync() for instance.
Question:
Is there any difference between the two in this scenario? Any help or input appreciated, thanks!
Task.Run may post the operation to be processed at a different thread. That's the only difference.
This may be of use - for example, if LongProcess isn't truly asynchronous, it will make the caller return faster. But for a truly asynchronous method, there's no point in using Task.Run, and it may result in unnecessary waste.
Be careful, though, because the behaviour of Task.Run will change based on overload resolution. In your example, the Func<Task> overload will be chosen, which will (correctly) wait for LongProcess to finish. However, if a non-task-returning delegate was used, Task.Run will only wait for execution up to the first await (note that this is how TaskFactory.StartNew will always behave, so don't use that).
Quite often people think that async-await is done by several threads. In fact it is all done by one thread.
See the addition below about this one thread statement
The thing that helped me a lot to understand async-await is this interview with Eric Lippert about async-await. Somewhere in the middle he compares async await with a cook who has to wait for some water to boil. Instead of doing nothing, he looks around to see if there is still something else to do like slicing the onions. If that is finished, and the water still doesn't boil he checks if there is something else to do, and so forth until he has nothing to do but wait. In that case he returns to the first thing he waited for.
If your procedure calls an awaitable function, we are certain that somewhere in this awaitable function there is a call to an awaitable function, otherwise the function wouldn't be awaitable. In fact, your compiler will warn you if you forget to await somewhere in your awaitable function.
If your awaitable function calls the other awaitable function, then the thread enters this other function and starts doing the things in this function and goes deeper into other functions until he meets an await.
Instead of waiting for the results, the thread goes up in his call stack to see if there are other pieces of code he can process until he sees an await. Go up again in the call stack, process until await, etc. Once everyone is awaiting the thread looks for the bottom await and continues once that is finished.
This has the advantage, that if the caller of your awaitable function does not need the result of your function, but can do other things before the result is needed, these other things can be done by the thread instead of waiting inside your function.
A call without waiting immediately for the result would look like this:
private async Task MyFunction()
{
Task<ReturnType>taskA = SomeFunctionAsync(...)
// I don't need the result yet, I can do something else
DoSomethingElse();
// now I need the result of SomeFunctionAsync, await for it:
ReturnType result = await TaskA;
// now you can use object result
}
Note that in this scenario everything is done by one thread. As long as your thread has something to do he will be busy.
Addition. It is not true that only one thread is involved. Any thread who has nothing to do might continue processing your code after an await. If you check the thread id, you can see that this id can be changed after the await. The continuing thread has the same context as the original thread, so you can act as if it was the original thread. No need to check for InvokeRequired, no need to use mutexes or critical sections. For your code this is as if there is one thread involved.
The link to the article in the end of this answer explains a bit more about thread context
You'll see awaitable functions mainly where some other process has to do things, while your thread just has to wait idly until the other thing is finished. Examples are sending data over the internet, saving a file, communicating with a database etc.
However, sometimes some heavy calculations has to be done, and you want your thread to be free to do something else, like respond to user input. In that case you can start an awaitable action as if you called an async function.
Task<ResultType> LetSomeoneDoHeavyCalculations(...)
{
DoSomePreparations()
// start a different thread that does the heavy calculations:
var myTask = Task.Run( () => DoHeavyCalculations(...))
// now you are free to do other things
DoSomethingElse();
// once you need the result of the HeavyCalculations await for it
var myResult = await myTask;
// use myResult
...
}
Now a different thread is doing the heavy calculations while your thread is free to do other things. Once it starts awaiting your caller can do things until he starts awaiting. Effectively your thread will be fairly free to react on user input. However, this will only be the case if everyone is awaiting. While your thread is busy doing things your thread can't react on user input. Therefore always make sure that if you think your UI thread has to do some busy processing that takes some time use Task.Run and let another thread do it
Another article that helped me: Async-Await by the brilliant explainer Stephen Cleary
This answer deals with the specific case of awaiting an async method in the event handler of a GUI application. In this case the first approach has a significant advantage over the second. Before explaining why, lets rewrite the two approaches in a way that reflects clearly the context of this answer. What follows is only relevant for event handlers of GUI applications.
private async void Button1_Click(object sender, EventArgs args)
{
await Task.Run(async () => await LongProcessAsync());
}
vs
private async void Button1_Click(object sender, EventArgs args)
{
await LongProcessAsync();
}
I added the suffix Async in the method's name, to comply with the guidlines. I also made async the anonymous delegate, just for readability reasons. The overhead of creating a state machine is minuscule, and is dwarfed by the value of communicating clearly that this Task.Run returns a promise-style Task, not an old-school delegate Task intended for background processing of CPU-bound workloads.
The advantage of the first approach is that guarantees that the UI will remain responsive. The second approach offers no such guarantee. As long as you are using the build-in async APIs of the .NET platform, the probability of the UI being blocked by the second approach is pretty small. After all, these APIs are implemented by experts¹. By the moment you start awaiting your own async methods, all guarantees are off. Unless of course your first name is Stephen, and your surname is Toub or Cleary. If that's not the case, it is quite possible that sooner or later you'll write code like this:
public static async Task LongProcessAsync()
{
TeenyWeenyInitialization(); // Synchronous
await SomeBuildInAsyncMethod().ConfigureAwait(false); // Asynchronous
CalculateAndSave(); // Synchronous
}
The problem obviously is with the method TeenyWeenyInitialization(). This method is synchronous, and comes before the first await inside the body of the async method, so it won't be awaited. It will run synchronously every time you call the LongProcessAsync(). So if you follow the second approach (without Task.Run), the TeenyWeenyInitialization() will run on the UI thread.
How bad this can be? The initialization is teeny-weeny after all! Just a quick trip to the database to get a value, read the first line of a small text file, get a value from the registry. It's all over in a couple of milliseconds. At the time you wrote the program. In your PC. Before moving the data folder in a shared drive. Before the amount of data in the database became huge.
But you may get lucky and the TeenyWeenyInitialization() remains fast forever, what about the second synchronous method, the CalculateAndSave()? This one comes after an await that is configured to not capture the context, so it runs on a thread-pool thread. It should never run on the UI thread, right? Wrong. It depends to the Task returned by SomeBuildInAsyncMethod(). If the Task is completed, a thread switch will not occur, and the CalculateAndSave() will run on the same thread that called the method. If you follow the second approach, this will be the UI thread. You may never experience a case where the SomeBuildInAsyncMethod() returned a completed Task in your development environment, but the production environment may be different in ways difficult to predict.
Having an application that performs badly is unpleasant. Having an application that performs badly and freezes the UI is even worse. Do you really want to risk it? If you don't, please use always Task.Run(async inside your event handlers. Especially when awaiting methods you have coded yourself!
¹ Disclaimer, some built-in async APIs are not properly implemented.
Important: The Task.Run runs the supplied asynchronous delegate on a ThreadPool thread, so it's required that the LongProcessAsync has no affinity to the UI thread. If it involves interaction with UI controls, then the Task.Runis not an option. Thanks to #Zmaster for pointing out this important subtlety in the comments.
I've seen how the await keyword is implemented and resulting structure it creates. I think I have a rudimentary understanding of it. However, is
public async Task DoWork()
{
await this.Operation1Async();
await this.Operation2Async();
await this.Operation3Async();
}
"better" (generally speaking) or
public async Task DoWork()
{
await this.Operation1Async();
this.Operation2();
this.Operation3();
}
The problem with the first approach is that it is creating a new Task for each await call? Which entails a new thread?
Whereas the first creates a new Task on the first await and then everything from there is processed in the new Task?
Edit
Ok maybe I wasn't too clear, but if for example we have
while (await reader.ReadAsync())
{
//...
}
await reader.NextResultAsync();
// ...
Is this not creating two tasks? One in the main thread with the first ReadAsync then another task in this newly created task with the NextResultAsync. My question is there really a need for the second task, isn't
the one task created in the main thread sufficient? So
while (await reader.ReadAsync())
{
//...
}
reader.NextResult();
// ...
it is creating a new Task for each await call? Which entails a new thread?
Yes and no. Yes, it is creating a Task for each asynchronous method; the async state machine will create one. However, these tasks are not threads, nor do they even run on threads. They do not "run" anywhere.
You may find some blog posts of mine useful:
async intro, which explains how async/await work.
There Is No Thread, which explains how tasks can work without threads.
Intro to the Task type, which explains how some tasks (Delegate Tasks) have code and run on threads, but the tasks used by async (Promise Tasks) do not.
Whereas the first creates a new Task on the first await and then everything from there is processed in the new Task?
Not at all. Tasks only complete once, and the method will not continue past the await until that task is complete. So, the task returned by Operation1Async has already completed before Operation2 is even called.
The 2 examples are not functionally equivalent so you would choose the one depending on your specific needs. In the first example the 3 tasks are executed sequentially, whereas in the second example the second and third tasks are running in parallel without waiting for their result to complete. Also in the second example the DoWork method could return before the second and third tasks have completed.
If you want to ensure that the tasks have completed before leaving the DoWork method body you might need to do this:
public async Task DoWork()
{
await this.Operation1Async();
this.Operation2().GetAwaiter().GetResult();
this.Operation3().GetAwaiter().GetResult();
}
which of course is absolutely terrible and you should never be doing it as it is blocking the main thread in which case you go with the first example. If those tasks use I/O completion ports then you should definitely take advantage of them instead of blocking the main thread.
If on the other hand you are asking whether you should make Operation2 and Operation3 asynchronous, then the answer is this: If they are doing I/O bound stuff where you can take advantage of I/O Completion Ports then you should absolutely make them async and go with the first approach. If they are CPU bound operations where you cannot use IOCP then it might be better to leave them synchronous because it wouldn't make any sense to execute this CPU bound operations in a separate task which you would block for anyway.
The problem with the first approach is that it is creating a new Task for each await call? Which entails a new thread?
This is your misunderstanding, which is leading to you to be suspicious of the code in the first example.
A Task does not entail a new thread. A Task certainly can be run on a new thread if you want to do so, but an important use of tasks is when a task directly or indirectly works through asynchronous i/o, which happens when the task, or one that it in turn awaits on, uses async i/o to access files or network streams (e.g. web or database access) allowing a pooled thread to be returned to the pool until that i/o has completed.
As such if the task does not complete immediately (which may happen if e.g. its purpose could be fulfilled entirely from currently-filled buffers) the thread currently running it can be returned to the pool and can be used to do something else in the meantime. When the i/o completes then another thread from the pool can take over and complete that waiting, which can then finish the waiting in a task waiting on that, and so on.
As such the first example in your question allows for fewer threads being used in total, especially when other work will also being using threads from the same pool.
In the second example once the first await has completed the thread that handled its completion will be blocking on the synchronous equivalent methods. If other operations also need to use threads from the pool then that thread not being returned to it, fresh threads will have to be spun up. As such the second example is the example that will need more threads.
One is not better than the other, they do different things.
In the first example, each operation is scheduled and performed on a thread, represented by a Task. Note: It's not guaranteed what thread they happen on.
The await keyword means (loosely) "wait until this asynchronous operation has finished and then continue". The continuation, is not necessarily done on the same thread either.
This means example one, is a synchronous processing of asynchronous operations. Now just because a Task is created, it doesn't infer a Thread is also created, there is a pool of threads the TaskScheduler uses, which have already been created, very minimal overhead is actually introduced.
In your second example, the await will call the first operation using the scheduler, then call the next two as normal. No Task or Thread is created for the second two calls, nor is it calling methods on a Task.
In the first example, you can also look into making your asynchronous calls simultaneous. Which will schedule all three operations to run "at the same time" (not guaranteed), and wait until they have all finished executing.
public async Task DoWork()
{
var o1 = this.Operation1Async();
var o2 = this.Operation2Async();
var o3 = this.Operation3Async();
await Task.WhenAll(o1, o2, o3);
}
I have created a list of Task, like this:
public void A()
{
}
public void B()
{
}
public void C()
{
}
public void Ex()
{
Task.WaitAll(Task.Factory.StartNew(A), Task.Factory.StartNew(B), Task.Factory.StartNew(C));
var p=true;
}
Now my question is that. Will all the Tasks inside the list execute one by one or will they execute in parallel.
p=true
"p" is set when all tasks are done or before they are done?
For the first question:
Will those tasks execute one by one or asynchronously.
(here, I imagine you meant concurrently, which is not exactly the same)
Using StartNew will run your task in the current TaskScheduler. by default that means it will use the ThreadPool and, if there are any available slots in the thread pool, it will be run in parallel. If all slots are taken in the task pool, it may throttle the execution of the tasks in order to avoid the CPU to be overwhelmed, and the tasks may not be executed at the same concurrently: there are no guarantees.
This is a simplified explanation and a more complete and detailed explanation on the scheduling strategy is explained on the TaskScheduler documentation.
As a side note. The documentation for StartTask mentions a subtle difference between StartNew(Action) and Run(Action). They are not exactly equivalent, unlike stated in other answers.
Starting with the .NET Framework 4.5, you can use the Task.Run(Action) method as a quick way to call StartNew(Action) with default parameters. Note, however, that there is a difference in behavior between the two methods regarding : Task.Run(Action) by default does not allow child tasks started with the TaskCreationOptions.AttachedToParent option to attach to the current Task instance, whereas StartNew(Action) does.
For the second question
"p" is set when all tasks are done or before they are done?
The short answer is yes.
However, you should consider using another approach as this one will block your thread and waiting idly. The alternative is to give the control back to the caller if you can and so the thread is freed and can be used by the CPU. this is especially true if the thread on which this code is running is part of a ThreadPool.
therefore, you should prefer using WhenAll(). It returns a Task, which can be awaited or on which ContinueWith can be called
example:
var tasks = new Task[] {Task.Factory.StartNew(A), Task.Factory.StartNew(B), Task.Factory.StartNew(C)};
await Task.WhenAll(tasks);
first:
you are creating the tasks in wrong way. when you instantiate a task you need to call Start method on it otherwise it dose nothing.
new Task(() => /* Something * /).Start();
if you create Tasks the way you just did (by calling constructor and hitting start or using the TaskFacotry or even Task.Run) by default a ThreadPool thread will be dedicated to the task and thus the task is executed in parallel.
the Task.WhenAll Method will block the execution of calling method until all tasks which are passed to it are done executing.
so the boolean variable is set after all tasks are done.