The typical way of writing an async Task method is as follows:
public async Task<int> LongCalculationAsync(int arg)
{
int res = doSomeLongCalculation();
res += await CallSomeOtherTaskAsync();
res ++;
return res;
}
When written like this, the first part (before the await) is performed synchronously, and then another Task is created and started on perhaps another thread, which is then continued by a task that contains the last 2 lines and is run on the original context.
The problem is that the synchronous part is performed on whichever scheduler the caller runs on. But my problem is that I know that I want the task returned to run using a specific scheduler, even if the method is called from the UI thread.
Is there a way for the async method itself to decide on the context?
Use Task.Factory.StartNew to push work to any scheduler you want. For the default thread-pool use Task.Run. It's quite easy:
await Task.Factory.StartNew(() => doSomeLongCalculation(), ...)
Related
So I have this WrapperFunction that tries to make a FunctionReturningVoid to be called asynchronously:
public async Task WrapperFunction()
{
this.FunctionReturningVoid("aParameter");
}
This is the function that returns nothing. In some parts of the code (not detailed here) it is called SYNChronously but in the CallerFunction() we want it to be run ASYNChronously.
public void FunctionReturningVoid(string myString)
{
Console.Write(myString);
}
This is the function that has the async implemented and needs to have WrapperFunction do its things without blocking otherStuff().
public async Task CallerFunction()
{
await WrapperFunction():
int regular = otherStuff();
...
}
The IDE is warning me that WrapperFunction is not using await:
This async method lacks 'await' operators and will run synchronously.
Consider using the 'await' operator to await non-blocking API calls,
or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Question: How to use async without using await in WrapperFunction? If I use await it tells me that cannot await void.
It's important to distinguish asynchronous from parallel.
Asynchronous means not blocking the current thread while you're waiting for something to happen. This lets the current thread go do something else while waiting.
Parallel means doing more than one thing at the same time. This requires separate threads for each task.
You cannot call FunctionReturningVoid asynchronously because it is not an asynchronous method. In your example, Console.WriteLine() is written in a way that will block the thread until it completes. You can't change that. But I understand that's just your example for this question. If your actual method is doing some kind of I/O operation, like a network request or writing a file, you could rewrite it to use asynchronous methods. But if it's doing CPU-heavy work, or you just can't rewrite it, then you're stuck with it being synchronous - it will block the current thread while it runs.
However, you can run FunctionReturningVoid in parallel (on another thread) and wait for it asynchronously (so it doesn't block the current thread). This would be wise if this is a desktop application - you don't want to lock up your UI while it runs.
To do that, you can use Task.Run, which will start running code on another thread and return a Task that you can use to know when it completes. That means your WrapperFunction would look like this:
public Task WrapperFunction()
{
return Task.Run(() => this.FunctionReturningVoid("aParameter"));
}
Side point: Notice I removed the async keyword. It's not necessary since you can just pass the Task to the calling method. There is more information about this here.
Microsoft has some well-written articles about Asynchronous programming with async and await that are worth the read.
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.
I have an asynchronous method that does something peculiar (at least, looks peculiar to me):
public async ReturnType MethodNameHere()
{
var result = await DoSomethingAsync(); // this could take a while (dozens of seconds)!
// no other processing after awaiting the result of the asynchronous task
return result;
}
This method is consumed by another method, as follows (meant to run in an infinite loop):
private async void ProcessSomething()
{
// some console printing
var returnType = await MethodNameHere();
// do some _actual_ work here, process stuff, etc
ProcessSomething(); // call the same method again
}
Now this task is running via Task.Factory.StartNew(ProcessSomething, TaskCreationOptions.LongRunning), which, along with other tasks that do similar work, are combined with Task.Factory.ContinueWhenAll.
Question
Is it correct to assume that, if the scheduler ends up putting each task on the same thread as one another, the tasks will not block each other?
If that is the case, is there even any benefit to calling the asynchronous version of DoSomething if it is to be running "alone" in a Task?
If you want to do some work before the async operation finishes, just do that work before awaiting the returned Task.
However, the primary purpose of async operations is to avoid consuming threads while waiting for non-blocking operations.
I have a Task that should run asynchronously but runs synchronously.
I've created a Task:
var task = Task<int>.Factory.FromAsync(proxy.BeginSaveImage(sp, new AsyncCallback(CompleteSave), state), proxy.EndSaveImage);
int res = task.Result;
The Task calls an asynchronous WCF service. The WCF Service function:
public IAsyncResult BeginSaveImage(statePackage sp, AsyncCallback callback, object state)
{
gStatePackage = sp;
// Create a task to do the work
var task = Task<int>.Factory.StartNew(this.SaveImage, state);
return task.ContinueWith(res => callback(task));
}
I run the task inside a loop.
My problem is that when I run it, it doesn't run in parallel and each call for task.Results waits for the task to be complete before continuing on.
When I put the Task code in a function SaveImageProcedure and call it from within the loop like that:
Task.Factory.StartNew(() =>
{
SaveImageProcedure(sp);
});
It runs asynchronously. I don't want to wrap an async call with another async wrapper. Why the call using task.Result doesn't run asynchronously and how can I change it to run async without wrapping it like I did to run async (or if it runs async, don't wait for results and continue the operation)?
I don't need the task returned value, and don't mind getting it but with the code continue to run for the next iteration in the loop without waiting for the result to continue.
By reading the Result field, you are waiting for the result to be available.
Try evaluating the result in a Continue or, use await
I'd expect something that looks like (pseudo code):
var task = Task<int>...
task.ContinueWith(result => {
if (result != expected) throw new Exception("...");
});
Essentially, if you are doing one task and you have to get the result, you will be doing this part of the work flow synchronously. Task and async only allow you to do other things while you wait, they don't do any magic.
I recommend you read my async intro and MSDN article on best practices. In particular, use await instead of ContinueWith or Result.
On the server side, there's no point in wrapping the work inside StartNew. It should just execute synchronously if it has only synchronous work to do.
Not sure if I am messed up with my understanding of how async await works, but here is the problem I am stucked at.
Consider a contrived example
This code blocks UI
public async void LoginButtonClicked()
{
//create a continuation point so every following statement will get executed as ContinueWith
await Task.FromResult(0);
//this call takes time to execute
Remote.Login("user","password");
}
But this does not (obviously)
public void LoginButtonClicked()
{
Task.Run(()=>{ Remote.Login("user","password");});
}
I like to use method 1 because I don't want to spin long work using a Task.Run rather I prefer framework handle this form me. But the problem is The call to Method 1 seems blocking.
Using await/async only stops you from blocking the UI if all the long-running operations you call are async. In your example your Remote.Login is a synchronous call, so regardless of what the prior await line does, this will block your UI.
You need to either get an async version of your actual long-running operation (eg something returning a Task) or if that is not possible, then you can resort to Task.Run in order to move this work to the ThreadPool.
What you want if possible:
public async void LoginButtonClicked()
{
await Remote.LoginAsync("user","password");
// do anything else required
}
Every async method has its context.
When the Task starts it might run in a new SynchronizationContext. "Might" because if the task is already completed, like Task.FromResult(0), then no other SynchronizationContext is created and the original one is used.
Awaiting for a task means that when the task is finished, the next statement will run in the original SynchronizationContext.
This behavior can be changed using the Task.ConfigureAwait(continueOnCapturedContext: false). This means that the next statement will continue on the same context. But this will change nothing by doing Task.FromResult(0).ConfigureAwait(false) because the task is already completed and the original context will be used.
Therefore your Remote.Login("user","password"); will be run in the original context, thus blocking the UI thread which runs on the same context.
If you would have something like:
public async void LoginButtonClicked()
{
await Task.Delay(5000).ConfigureAwait(false);
Remote.Login("user","password");
}
Then Remote.Login("user","password"); would execute in the thread pool context thus being on a different context than the original UI context.
So the best way to fix your code is to create a Remote.LoginAsync() as stated in #Nicholas W answer.
NOTE on performance: if you have an async method with multiple await statements, and you don't need some of those awaits to do work on UI or web app thread, then you can use Task.ConfigureAwait(false) in order to prevent multiple switches to the UI/web-app context which slices its execution time.
You run in parallel Task.FromResult(0); and still wait for Remote.Login("user","password"); to be finished
You run Remote.Login("user","password"); asynchronously.
You have to create async version of Remote.Login
async Task LoginAsync(string user, string password)
{
Remote.Login(user, password);
await Task.FromResult(0);
}
and call it
public async void LoginButtonClicked()
{
await LoginAsync("user", "password");
}