When do multiple awaits make sense? - c#

I have some misunderstanding with c# async/await mechanism.
Is there any essential difference between
private async void Init()
{
await Task.Run(() => Do1());
await Task.Run(() => Do2());
}
and
private async void Init()
{
await Task.Run(() =>
{
Do1();
Do2();
});
}
The only difference I see: in the first sample Do1 and Do2 will be run in different threads while in the second sample - in the same thread. But again, what is the real benefit of it and when I should prefer the 1st approach over the second one and vice versa?
EDIT: The second case
What is the difference between
private async void Init()
{
await Task.Run(() => Do1());
Do3();
}
and
private async void Init()
{
await Task.Run(() =>
{
Do1();
Do3();
});
}

The difference is:
First example:
You queue Do1 on a threadpool thread and asynchronously wait for it to complete, then do the exact same with Do2. These may run on different threads.
You queue Do1 and Do2 to execute synchronously one after the other on the same thread pool thread.
Second example:
Queue Do1 on the threadpool and asynchronously wait for it to complete, then invoke Do3 synchronously.
This is exactly the same as the second part of the first example.
Note that when you await, you asynchronously wait for the operation to complete, hence unless the method finishes it won't execute the next line of code.
I'm assuming you're asking yourself if one is preferable to the other, and as in most cases, it depends. If you're running inside a console app, and you're going to asynchronously wait for Do1 to complete anyway, then pass both methods to the same Task.Run invocation. If you're planning on doing this in a place where synchronization matters, such as a GUI application, then any operation which needs to interact with UI controls should be invoked on the UI thread.
Another option which is more common is when you have two operations which are independent of each other and you want to start them together and wait for both to complete. This is where you'd use Task.WhenAll:
var firstDo = Task.Run(() => Do1());
var secondDo = Task.Run(() => Do2());
await Task.WhenAll(firstDo, secondDo);
Side note:
Do not use async void in asynchronous methods with no return value, that is what async Task is for. The former is only meant to allow compatibility with event handlers, where I'm assuming this isn't the case.

if Do1() and Do2() are independent from each other you should run them separately. This is even more important if they're long-running / blocking processes
on the other hand if they're related and the first one does some operations that are required by the other then it is better to run them in one thread to reduce the threading overhead. After all, you know in advance they need to run sequentially

Related

Continuation is running before antecedents have finished

I have two long running tasks that I want to execute, to be followed with a continuation. The problem I am having is that the continuation is running before the two long running tasks have signalled. Those two tasks are not erroring, they've barely started when the continuation commences.
The intent is that the first two tasks can run in parallel, but the continuation should only run once they've both finished.
I have probably overlooked something really simple, but near as I can tell I am following the patterns suggested in the question Running multiple async tasks and waiting for them all to complete. I realise I could probably dispense with the .ContinueWith() as the code following the Task.WhenAll() will implicitly become a continuation, but I prefer to keep the explicit continuation (unless it is the cause of my issue).
private async Task MyMethod()
{
var tasks = new List<Task>
{
new Task(async () => await DoLongRunningTask1()),
new Task(async () => await DoLongRunningTask2())
};
Parallel.ForEach(tasks, async task => task.Start());
await Task.WhenAll(tasks)
.ContinueWith(async t =>{
//another long running bit of code with some parallel
//execution based on the results of the first two tasks
}
, TaskContinuationOptions.OnlyOnRanToCompletion);
}
private async Task DoLongRunningTask1()
{
... long running operation ...
}
private async Task DoLongRunningTask2()
{
... another long running operation ...
}
I appreciate any help or suggestions!
Your code has multiple issues:
Creating tasks with the non-generic Task constructor, with async delegate. This constructor accepts an Action, not a Func<Task>, so your async delegate is actually an async void, which is something to avoid. You can fix this by creating nested Task<Task>s, where the outer task represents the launching of the inner task.
Using the Parallel.ForEach with async delegate. This again results in async void delegate, for the same reason. The correct API to use when you want to parallelize asynchronous delegates is the Parallel.ForEachAsync, available from .NET 6 and later.
Using parallelism to start tasks. Starting a task with Start is not CPU-bound. The Start doesn't execute the task, it just schedules it for execution, normally on the ThreadPool. So you can replace the Parallel.ForEach with a simple foreach loop or the List.ForEach.
Awaiting an unwrapped ContinueWith continuation that has an async delegate. This continuation returns a nested Task<Task>. You should Unwrap this nested task, otherwise you are awaiting the launching of the asynchronous operation, not its completion.
Here is your code "fixed":
private async Task MyMethod()
{
List<Task<Task>> tasksOfTasks = new()
{
new Task<Task>(async () => await DoLongRunningTask1()),
new Task<Task>(async () => await DoLongRunningTask2())
};
tasksOfTasks.ForEach(taskTask => taskTask.Start());
await Task.WhenAll(tasksOfTasks.Select(taskTask => taskTask.Unwrap()))
.ContinueWith(async t =>
{
//another long running bit of code with some parallel
//execution based on the results of the first two tasks
}, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap();
}
In case of failure in the DoLongRunningTask1 or the DoLongRunningTask2 methods, this code will most likely result in a TaskCanceledException, which is what happens when you pass the TaskContinuationOptions.OnlyOnRanToCompletion flag and the antecedent task does not run to completion successfully. This behavior is unlikely to be what you want.
The above code also violates the guideline CA2008: Do not create tasks without passing a TaskScheduler. This guideline says that being depended on the ambient TaskScheduler.Current is not a good idea, and specifying explicitly the TaskScheduler.Default is recommended. You'll have to specify it in both the Start and ContinueWith methods.
If you think that all this is overly complicated, you are right. That's not the kind of code that you want to write and maintain, unless you have spent months studying the Task Parallel Library, you know it in and out, and you have no other higher-level alternative. It's the kind of code that you might find in specialized libraries, not in application code.
You are overusing async. Below is more cleaner rewrite of what you have done above.
private async Task MyMethod()
{
var tasks = new Task[] {DoLongRunningTask1(), DoLongRunningTask2()];
await Task.WhenAll(tasks);
//another long running bit of code with some parallel
//execution based on the results of the first two tasks
}
private async Task DoLongRunningTask1()
{
... long running operation ...
}
private async Task DoLongRunningTask2()
{
... another long running operation ...
}
I assumed DoLongRunningTask's are async in nature, if they are syncronious then start them using Task.Run within a ThreadPool thread and use resulting Task.

What does accessing a Result on a Task before calling Wait actually do?

var task = Task.Run(() => DoSomeStuff()).Result;
What happens here under the hood?
I made a tiny test:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var r = Task.Run( () => {Thread.Sleep(5000); return 123; }).Result;
Console.WriteLine(r);
}
}
It prints "123" after 5s. So does accessing any such property on Task act as a shortcut to calling Task.Wait() i.e. is this safe to do?
Previously my code called Task.Delay(5000) which returned "123" immediately. I fixed this in my question but leave this here as comments and answers reference it.
You asked two questions. First, does accessing Result implicitly cause a synchronous Wait? Yes. But the more important question is:
is this safe to do?
It is not safe to do this.
It is very easy to get into a situation where the task that you are synchronously waiting on has scheduled work to run in the future before it completes onto the thread that you just put to sleep. Now we have a situation where the thread will not wake up until the sleeping thread does some work, which it never does because it is asleep.
If you already know that the task is complete then it is safe to synchronously wait for the result. If you do not, then it is not safe to synchronously wait.
Now, you might say, suppose I know through some other means that it is safe to wait synchronously for an incomplete task. Then is it safe to wait? Well, by the assumption of the question, yes, but it still might not be smart to wait. Remember, the whole point of asynchrony is to manage resources efficiently in a world of high latency. If you are synchronously waiting for an asynchronous task to complete then you are forcing a worker to sleep until another worker is done; the sleeping worker could be doing work! The whole point of asynchrony is to avoid situations where workers go idle, so don't force them to.
An await is an asynchronous wait. It is a wait that means "wait on running the rest of the current workflow until after this task is done, but find something to do while you are waiting". We did a lot of work to add it to the language, so use it!
So does accessing any such property on Task act as a shortcut to calling Task.Wait()?
Yes.
From the docs:
Accessing the [Result] property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
However, your test doesn't do what you think it does.
Task.Delay(..) returns a Task which completes after the specified amount of time. It doesn't block the calling thread.
So () => { Task.Delay(5000); return 123; } simply creates a new Task (which will complete in 5 seconds), then throws it away and immediately returns 123.
You can either:
Block the calling thread, by doing Task.Delay(5000).Wait() (which does the same thing as Thread.Sleep(5000))
Asynchronously wait for the Task returned from Task.Delay to complete: Task.Run(async () => { await Task.Delay(5000); return 123; })
The test doesn't wait for Task.Delay() so it returns immediatelly. It should be :
var r = Task.Run(async () => { await Task.Delay(5000); return 123; }).Result;
The behavior of Result is well defined - if the task hasn't completed, it blocks until it does. Accessing other Task properties doesn't block

Why do I get a warning when trying to run an async method on another thread?

I have an async method, which calls another async method, however, I want it run on a separate thread in parallel:
public async Task<Page> ServePage() {
Task.Run(() => DoThings(10)); // warning here
// ... other code
return new Page();
}
public async Task DoThings(int foo) {
// stuff
}
The warning states:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
That is, in fact, what I am trying to do. Why am I getting a compiler warning? Is the syntax of Task.Run incorrect?
TL;DR
The reason why you get the warning is because
Task.Run(() => DoThings(10)); // warning here
returns a Task, and since your ServePage method is marked as async, the compiler believes that you should await the result of the Task
Detail
You're mixing two very different paradigms, which coincidentally both involve Task, viz:
Task.Run(), which is generally useful for parallelizing CPU bound work by utilizing multiple cores which may be available
async / await, which is useful for waiting for I/O bound operations to complete, without blocking (wasting) a thread.
So for instance, if you wanted to do 3 x CPU bound operations concurrently, and since Task.Run returns a Task, what you could do is:
public Page ServePage() // If we are CPU bound, there's no point decorating this as async
{
var taskX = Task.Run(() => CalculateMeaningOfLife()); // Start taskX
var taskY = Task.Run(() => CalculateJonSkeetsIQ()); // Start taskY
var z = DoMoreHeavyLiftingOnCurrentThread();
Task.WaitAll(taskX, taskY); // Wait for X and Y - the Task equivalent of `Thread.Join`
// Return a final object comprising data from the work done on all three tasks
return new Page(taskX.Result, taskY.Result, z);
}
The above is likely to utilise up to three threads, which could do the CPU bound work concurrently if there are sufficient cores to do so. Note however that using multiple threads concurrently will reduce the scalability of your system, since fewer simultaneous pages can be served without context switching.
This is in contrast to async / await, which is generally used to free up threads while waiting for I/O bound calls to complete. Async is commonly used in Api and Web apps to increase scalability as the thread is released for other work while the IO bound work happens.
Assuming DoThings is indeed I/O bound, we can do something like:
public async Task<string> DoThings(int foo) {
var result = await SomeAsyncIo(foo);
return "done!";
}
Async work can also be done in parallel:
public async Task<Page> ServePage() {
var task1 = DoThings(123); // Kick off Task 1
var task2 = DoThings(234); // Kick off Task 2 in parallel with task 1
await Task.WhenAll(task1, task2); // Wait for both tasks to finish, while releasing this thread
return new Page(task1.Result, task2.Result); // Return a result with data from both tasks
}
If the I/O bound work takes a reasonable amount of time, there's a good chance there's a point during the await Task.WhenAll when ZERO threads are actually running - See Stephen Cleary's article.
There's a 3rd, but very dangerous option, which is fire and forget. Since method DoThings is already marked as async, it already returns a Task, so there's no need at all to use Task.Run at all. Fire and forget would look as follows:
public Page ServePage() // No async
{
#pragma warning disable 4014 // warning is suppresed by the Pragma
DoThings(10); // Kick off DoThings but don't wait for it to complete.
#pragma warning enable 4014
// ... other code
return new Page();
}
As per #JohnWu's comment, the 'fire and forget' approach is dangerous and usually indicates a design smell. More on this here and here
Edit
Re:
there is nuance to this that escapes me over and over, such as that calling an async method that returns Task from a synchronous method fires-and-forgets execution of the method. (That's the very last code sample.) Am I understanding that correctly?
It's a bit difficult to explain, but irrespective of whether called with, or without the await keyword, any synchronous code in an invoked async method before the first await will be executed on the caller's thread, unless we resort to hammers like Task.Run.
Perhaps this example might help the understanding (note that we're deliberately using synchronous Thread.Sleep and not await Task.Delay to simulate CPU bound work and introduce latency which can be observed)
public async Task<Page> ServePage()
{
// Launched from this same thread,
// returns after ~2 seconds (i.e. hits both sleeps)
// continuation printed.
await DoThings(10);
#pragma warning disable 4014
// Launched from this same thread,
// returns after ~1 second (i.e. hits first sleep only)
// continuation not yet printed
DoThings(10);
// Task likely to be scheduled on a second thread
// will return within few milliseconds (i.e. not blocked by any sleeps)
Task.Run(() => DoThings(10));
// Task likely to be scheduled on a second thread
// will return after 2 seconds, although caller's thread will be released during the await
// Generally a waste of a thread unless also doing CPU bound work on current thread, or unless we want to release the calling thread.
await Task.Run(() => DoThings());
// Redundant state machine, returns after 2 seconds
// see return Task vs async return await Task https://stackoverflow.com/questions/19098143
await Task.Run(async () => await DoThings());
}
public async Task<string> DoThings(int foo) {
Thread.Sleep(1000);
var result = await SomeAsyncIo(foo);
Trace.WriteLine("Continuation!");
Thread.Sleep(1000);
return "done!";
}
There's one other important point to note - in most cases, there are no guarantees that the continuation code AFTER an await will be executed on the same thread as that before the await. The continuation code is re-written by the compiler into a Task, and the continuation task will be scheduled on the thread pool.
This
Task.Run(() => DoThings(10));
will run your separate task "in parallel", meaning that another thread will run this task. The thread that entered this method will then continue execution of the next statement.
What you're doing here is allowed. That's why it's a warning. (I'm assuming that the rest of the method, not shown, returns a Page.)
The message is warning you that because the other task is executing on another thread, it could execute before, after, or at the same time as the other code in the method. This method no longer "knows" what the task is doing or when it's finished.
It's saying, in other words:
Don't assume that because this line of code appears before other lines of code in the method that it's going to execute before they do. If something needs to execute after this task finishes, await it before doing the next thing.
As an example:
public int DoSomething()
{
var x = 1;
Task.Run(() => x++);
return x;
}
What does this return? It depends. It could return 1 or 2. It may return either before or after x is incremented. If you care whether or not x has been incremented then this is bad. If your task does something that doesn't matter to the rest of the method and you don't care whether the task even finishes before or after the rest of the method, then this warning wouldn't matter to you. If you do care then it's important and you'd want to await the task.
All versions bellow are valid, warning free, and more or less equivalent:
public Task ServePage1()
{
return Task.Run(async () => await DoThings(10));
}
public async Task ServePage2()
{
await Task.Run(async () => await DoThings(10));
}
public Task ServePage3()
{
return Task.Run(() => DoThings(10));
}
public async Task ServePage4()
{
await Task.Run(() => DoThings(10));
}
In general you should not fire-and-forget tasks by ignoring the return value of Task.Run. If you do that the compiler will issue a warning, because it is rarely intentional.
When you call an async method that returns a Task, you can do two things:
Await the Task, which returns control to the caller immediately, and resumes when the Task completes (with or without a return value). This is also where you can trap any exceptions that might have happened when executing the Task.
Fire and Forget. This is when you start a Task and completely cut it loose - including the ability to trap exceptions. Most importantly, unless awaited, control execution will proceed beyond the call and can cause unintended state corruption issues.
You did #2 in your code. While technically allowed, because of the reasons cited above, the compiler warned you.
Question for you though - if you really just wanted to fire and forget, why did you need to create that explicit Task? You could just have called DoThings(10) directly, couldn't you? Unless I am missing something in the code that is not visible to me.
So - couldn't you have done just this?
public async Task<Page> ServePage() {
DoThings(10);
}

What's the difference between starting and awaiting a Task?

What's the difference between starting and awaiting? Code below taken from Stephen Cleary's blog (including comments)
public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = DoOperation2Async();
// At this point, all three tasks are running at the same time.
// Now, we await them all.
await Task.WhenAll(tasks);
}
I thought that the tasks begin running when you await them ... but the comments in the code seem to imply otherwise.
Also, how can the tasks be running after I just attributed them to an array of type Task. Isn't that just an attribution, by nature not involving action?
A Task returns "hot" (i.e. already started). await asynchronously waits for the Task to complete.
In your example, where you actually do the await will affect whether the tasks are ran one after the other, or all of them at the same time:
await DoOperation0Async(); // start DoOperation0Async, wait for completion, then move on
await DoOperation1Async(); // start DoOperation1Async, wait for completion, then move on
await DoOperation2Async(); // start DoOperation2Async, wait for completion, then move on
As opposed to:
tasks[0] = DoOperation0Async(); // start DoOperation0Async, move on without waiting for completion
tasks[1] = DoOperation1Async(); // start DoOperation1Async, move on without waiting for completion
tasks[2] = DoOperation2Async(); // start DoOperation2Async, move on without waiting for completion
await Task.WhenAll(tasks); // wait for all of them to complete
Update
"doesn't await make an async operation... behave like sync, in this example (and not only)? Because we can't (!) run anything else in parallel with DoOperation0Async() in the first case. By comparison, in the 2nd case DoOperation0Async() and DoOperation1Async() run in parallel (e.g. concurrency,the main benefits of async?)"
This is a big subject and a question worth being asked as it's own thread on SO as it deviates from the original question of the difference between starting and awaiting tasks - therefore I'll keep this answer short, while referring you to other answers where appropriate.
No, awaiting an async operation does not make it behave like sync; what these keywords do is enabling developers to write asynchronous code that resembles a synchronous workflow (see this answer by Eric Lippert for more).
Calling await DoOperation0Async() will not block the thread executing this code flow, whereas a synchronous version of DoOperation0 (or something like DoOperation0Async.Result) will block the thread until the operation is complete.
Think about this in a web context. Let's say a request arrives in a server application. As part of producing a response to that request, you need to do a long-running operation (e.g. query an external API to get some value needed to produce your response). If the execution of this long-running operation was synchronous, the thread executing your request would block as it would have to wait for the long-running operation to complete. On the other hand, if the execution of this long-running operation was asynchronous, the request thread could be freed up so it could do other things (like service other requests) while the long-running operation was still running. Then, when the long-running operation would eventually complete, the request thread (or possibly another thread from the thread pool) could pick up from where it left off (as the long-running operation would be complete and it's result would now be available) and do whatever work was left to produce the response.
The server application example also addresses the second part of your question about the main benefits of async - async/await is all about freeing up threads.
Isn't that just an attribution, by nature not involving action?
By calling the async method you execute the code within. Usually down the chain one method will create a Task and return it either by using return or by awaiting.
Starting a Task
You can start a Task by using Task.Run(...). This schedules some work on the Task Thread Pool.
Awaiting a Task
To get a Task you usually call some (async) Method that returns a Task. An async method behaves like a regular method until you await (or use Task.Run() ). Note that if you await down a chain of methods and the "final" method only does a Thread.Sleep() or synchronous operation - then you will block the initial calling thread, because no method ever used the Task's Thread Pool.
You can do some actual asynchronous operation in many ways:
using Task.Run
using Task.Delay
using Task.Yield
call a library that offers asynchronous operations
These are the ones that come to my mind, there are probably more.
By example
Let's assume that Thread ID 1 is the main thread where you are calling MethodA() from. Thread IDs 5 and up are Threads to run Tasks on (System.Threading.Tasks provides a default Scheduler for that).
public async Task MethodA()
{
// Thread ID 1, 0s passed total
var a = MethodB(); // takes 1s
// Thread ID 1, 1s passed total
await Task.WhenAll(a); // takes 2s
// Thread ID 5, 3s passed total
// When the method returns, the SynchronizationContext
// can change the Thread - see below
}
public async Task MethodB()
{
// Thread ID 1, 0s passed total
Thread.Sleep(1000); // simulate blocking operation for 1s
// Thread ID 1, 1s passed total
// the await makes MethodB return a Task to MethodA
// this task is run on the Task ThreadPool
await Task.Delay(2000); // simulate async call for 2s
// Thread ID 2 (Task's pool Thread), 3s passed total
}
We can see that MethodA was blocked on the MethodB until we hit an await statement.
Await, SynchronizationContext, and Console Apps
You should be aware of one feature of Tasks. They make sure to invoke back to a SynchronizationContext if one is present (basically non-console apps). You can easily run into a deadlock when using .Result or .Wait() on a Task if the called code does not take measures. See https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
async/await as syntactic sugar
await basically just schedules following code to run after the call was completed. Let me illustrate the idea of what is happening behind the scenes.
This is the untransformed code using async/await. The Something method is awaited, so all following code (Bye) will be run after Something completed.
public async Task SomethingAsync()
{
Hello();
await Something();
Bye();
}
To explain this I add a utility class Worker that simply takes some action to run and then notify when done.
public class Worker
{
private Action _action;
public event DoneHandler Done;
// skipping defining DoneHandler delegate
// store the action
public Worker(Action action) => _action = action;
public void Run()
{
// execute the action
_action();
// notify so that following code is run
Done?.Invoke();
}
}
Now our transformed code, not using async/await
public Task SomethingAsync()
{
Hello(); // this remains untouched
// create the worker to run the "awaited" method
var worker = new Worker(() => Something());
// register the rest of our method
worker.Done += () => Bye();
// execute it
worker.Run();
// I left out the part where we return something
// or run the action on a threadpool to keep it simple
}
Here's the short answer:
To answer this you just need to understand what the async / await keywords do.
We know a single thread can only do one thing at a time and we also know that a single thread bounces all over the application to various method calls and events, ETC. This means that where the thread needs to go next is most likely scheduled or queued up somewhere behind the scenes (it is but I won't explain that part here.) When a thread calls a method, that method is ran to completion before any other methods can be ran which is why long running methods are preferred to be dispatched to other threads to prevent the application from freezing. In order to break a single method up into separate queues we need to do some fancy programming OR you can put the async signature on the method. This tells the compiler that at some point the method can be broken up into other methods and placed in a queue to be ran later.
If that makes sense then you're already figuring out what await does... await tells the compiler that this is where the method is going to be broken up and scheduled to run later. This is why you can use the async keyword without the await keyword; although the compiler knows this and warns you. await does all this for you by use of a Task.
How does await use a Task tell the compiler to schedule the rest of the method? When you call await Task the compilers calls the Task.GetAwaiter() method on that Task for you. GetAwaiter() return a TaskAwaiter. The TaskAwaiter implements two interfaces ICriticalNotifyCompletion, INotifyCompletion. Each has one method, UnsafeOnCompleted(Action continuation) and OnCompleted(Action continuation). The compiler then wraps the rest of the method (after the await keyword) and puts it in an Action and then it calls the OnCompleted and UnsafeOnCompleted methods and passes that Action in as a parameter. Now when the Task is complete, if successful it calls OnCompleted and if not it calls UnsafeOnCompleted and it calls those on the same thread context used to start the Task. It uses the ThreadContext to dispatch the thread to the original thread.
Now you can understand that neither async or await execute any Tasks. They simply tell the compiler to use some prewritten code to schedule all of it for you. In fact; you can await a Task that's not running and it will await until the Task is executed and completed or until the application ends.
Knowing this; lets get hacky and understand it deeper by doing what async await does manually.
Using async await
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static async void Test()
{
Console.WriteLine($"Before Task");
await DoWorkAsync();
Console.WriteLine($"After Task");
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
Doing what the compiler does manually (sort of)
Note: Although this code works it is meant to help you understand async await from a top down point of view. It DOES NOT encompass or execute the same way the compiler does verbatim.
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static void Test()
{
Console.WriteLine($"Before Task");
var task = DoWorkAsync();
var taskAwaiter = task.GetAwaiter();
taskAwaiter.OnCompleted(() => Console.WriteLine($"After Task"));
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
LESSON SUMMARY:
Note that the method in my example DoWorkAsync() is just a function that returns a Task. In my example the Task is running because in the method I use return Task.Run(() =>…. Using the keyword await does not change that logic. It's exactly the same; await only does what I mentioned above.
If you have any questions just ask and I'll be happy to answer them.
With starting you start a task. That means it might be picked up for execution by whatever Multitasaking system is in place.
With waiting, you wait for one task to actually finish before you continue.
There is no such thing as a Fire and Forget Thread. You always need to come back, to react to exceptions or do somethings with the result of the asynchronous operation (Database Query or WebQuery result, FileSystem operation finished, Dokument send to the nearest printer pool).
You can start and have as many task running in paralell as you want. But sooner or later you will require the results before you can go on.

Task.Run does not work like Thread.start

I've been developing an application which I need to run some methods as parallel and not blocking. first I used Task.Run, but IN DEBUG MODE, I see that the operation blocks and just waits for the result. I do not want this, I want all method , which call in a foreach loop, run asynchronously.
public async void f()
{
foreach (var item in childrenANDparents)
{
await Task.Run(() => SendUpdatedSiteInfo(item.Host,site_fr));
// foreach loop does not work until the task return and continues
}
}
So I changed the task.run to thread.start and it works great!
public async void f()
{
foreach (var item in childrenANDparents)
{
Thread t = new Thread(() => SendUpdatedSiteInfo(item.Host, site_fr));
t.Start();
// foreach loop works regardless of the method, in debug mode it shows me
// they are working in parallel
}
}
Would you explain what is the difference and why ? I expect the same behavior from both code and it seems they are different.
thanks
I want all method , which call in a foreach loop, run asynchronously.
It seems that you're confusing async/sync calls with parallelization.
A quote from MSDN:
Data parallelism: A form of parallel processing where the same
computation executes in parallel on different data. Data parallelism
is supported in the Microsoft .NET Framework by the Parallel.For and
Parallel.ForEach methods and by PLINQ. Compare to task parallelism.
Asynchronous operation: An operation that that does not block the current thread
of control when the operation starts.
Let's have a closer look at your code again:
foreach (var item in childrenANDparents)
{
await Task.Run(() => SendUpdatedSiteInfo(item.Host,site_fr));
}
The await keyword will cause compiler to create a StateMachine that will handle the method execution.
It's like if you say to compiler:"Start this async operation without blocking any threads and when it's completed - execute the rest of the stuff".
After Task finishes execution this thread will be released and returned to a ThreadPool and it will execute the rest of the code on a first available thread from a ThreadPool and will make attempt to execute it in a thread in which it had started the method execution (unless .ConfigureAwait(false) is used in which case it's more like 'fire and forget' mode when we don't really care which thread will do the continuation).
When you create a separate Thread you do parallelism by delegating some code to run in a separate Thread. So depending on the code itself it may or may not be executed asynchronously.
It's like if you say to compiler:"Take this piece of work start a new thread and do it there"
If you still want to use Tasks with parallelism you could create an array of tasks in a loop and then wait for all of them to finish execution:
var tasks = new[]
{
childrenANDparents.Select(item=> Task.Run(() => SendUpdatedSiteInfo(item.Host,site_fr)));
}
await Task.WhenAll(tasks);
P.S.
And yes you may as well use TPL (Task Parallel Library) and specifically Parallel loops.
You could use a simple Parallel.ForEach or PLinq
Parallel.ForEach(childrenANDparents, (item) =>
{
SendUpdatedSiteInfo(item.Host,site_fr)
});
To better understand async and await its best to start reading some docos, its a large topic, but its worth your while
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Categories

Resources