Difference between when the callback runs in JavaScript vs C# - c#

JavaScript callback runs only when the call stack is empty. That happens either when the program execution finishes or when the main program calls await.
The callback in case of Task library in C#:
Assuming single thread program, behaves the same as JS (assuming await is used and not .Wait which would cause deadlock).
However in case of using Task.Run, or configure await false, the callback will run immediately when the response is ready. I am assuming each thread has its own call stack.
Is this correct distinction between when the callback runs in case of JS and C#?

Is this correct distinction between when the callback runs in case of JS and C#?
Not really.
In JavaScript, the continuation always runs on the main thread when it is not doing anything else. Even if you await a Promise that's already complete, it's as though you called setTimeout without a delay. The code always yields whenever there's an await. IMO, the JavaScript approach is cleaner and easier to understand than C#. However, the .NET team adopted a different approach for C#, I assume for performance reasons.
In C#, the first thing to note is that await may continue executing synchronously. If the awaitable is already complete, then there's no yielding that takes place and no continuation exists at all - the code just continues executing in the same method. If the awaitable is not already complete, then a continuation is attached to the awaitable along with the current context (SynchronizationContext.Current or TaskScheduler.Current).
When the awaitable completes, then it executes all its continuations. If it's possible, then the continuation is executed on the current thread (the one that completed the awaitable). If this kind of "inline continuation" isn't possible, then the continuation is queued to the context (SynchronizationContext / TaskScheduler). What happens then is entirely dependent on the context. E.g., UI contexts will queue continuations to the UI message processing loop, which is pretty much the same as setTimeout in JavaScript. As another example, the thread pool context will queue continuations to the thread pool.

Main difference is: single-thread vs multi-thread
I think, in your case, callback is called from different thread.
Learn more about TaskScheduler and SynchronizationContext to achieve your endeavour.

Related

C# await in UI thread

According to Microsoft Visual C# Step by Step 9th edition:
The await operator indicates that a method should be run by separate task,
and the calling code is suspended until the method call completes.
The thread used by the calling code is released and reused.
This is important if thread is the user interface thread, as it enables the user interface to remain responsive.
Suppose I have a method that is IO bound and frees up the CPU:
private async Task DoLongRunningIO()
{
...
}
Does the above paragraph mean that running
await DoLongRunningIO();
message.Text = "Done";
in the UI thread will still keep the UI responsive since the UI thread is released? (as opposed to DoLongRunningIO().Wait(), which would block the UI thread)
If the answer is yes, I assume it wouldn't be true if the long running task was CPU intensive instead, since the UI thread is still consumed in that case?
Does the above paragraph mean that running
await DoLongRunningIO();
message.Text = "Done";
in the UI thread will still keep the UI responsive since the UI thread is released?
Yes.
If the answer is yes, I assume it wouldn't be true if the long running task was CPU intensive instead, since the UI thread is still consumed in that case?
No, your assumption is incorrect. A "thread" and the "CPU" are not the same thing. Even if your machine has only a single CPU, there can be one CPU-intensive thread running, and the UI thread running, and the OS will share the CPU between them. You may find a slightly degradation in responsiveness, but the UI thread will still be able to run.
If you have more than one CPU core, as is the case with pretty much all modern hardware, then as long as the two threads don't interact, the UI thread will be able to run unimpeded, even if there is also a CPU-intensive thread running.
Note that this all assumes that the DoLongRunningIO() method was written correctly, i.e. does in fact reflect an operation that is handled asynchronously. For a CPU-intensive task, this would generally include a call to Task.Run() to execute the operation, though there are of course other mechanisms that could be used instead.
If that method is written incorrectly, then all bets are off. You didn't provide any details about that method, so it's impossible to say whether that's the case in your scenario.
Aside:
For what it's worth, I take issue with the phrasing "The await operator indicates that a method should be run by separate task" that you quoted from the book. The await operator says nothing about how a method or operation should be run/called/carried out/etc. The only thing that await does is indicate a point in the current method where, given an awaitable object (typically a Task), the method should return to the caller if the object is not in the completed state. It is up to whatever expression generates the awaitable object to handle how that task is created and whether it is done in a separate thread or otherwise in an asynchronous manner.

Is this a correct diagram of how async-await works?

I'm going to be trying to give a talk on async-await and I'm creating a flow chart that attempts to show the possible orders of execution.
I attempted to base that off the paragraphs
The beginning of an async method is executed just like any other
method. That is, it runs synchronously until it hits an “await” (or
throws an exception).
The “await” keyword is where things can get asynchronous. Await is
like a unary operator: it takes a single argument, an awaitable (an
“awaitable” is an asynchronous operation). Await examines that
awaitable to see if it has already completed; if the awaitable has
already completed, then the method just continues running
(synchronously, just like a regular method).
If “await” sees that the awaitable has not completed, then it acts
asynchronously. It tells the awaitable to run the remainder of the
method when it completes, and then returns from the async method.
Later on, when the awaitable completes, it will execute the remainder
of the async method. If you’re awaiting a built-in awaitable (such as
a task), then the remainder of the async method will execute on a
“context” that was captured before the “await” returned.
from http://blog.stephencleary.com/2012/02/async-and-await.html
The answer of usr is basically correct, though I think it makes too strong an analogy between threads and tasks. A task need not be anything like another thread. Remember, threads are workers, tasks are jobs. You can have a hundred things on your to-do list without hiring any workers to do them. Try to not think of tasks as lightweight workers, because they are not. They are jobs that need to be done; what worker does them is up to the code that handed you the task.
Your diagram starts off fine but it goes off the rails at "does the caller finish all independent work?" The continuation of the caller is, well, whatever it is. If that continuation involves doing work, it does work. Some of that work might be scheduling tasks to run on the current thread. Some of that work might be keeping the UI responsive.
Also, don't forget that the caller's thread could be terminated and the continuation of the task could be scheduled to another thread.
There are many, many things that can happen here; without understanding what exactly the caller is doing and what the thread context of the caller is, it is impossible to say what happens immediately after the await returns.
This
is vague and seems not correct.
What happens next inside the async method is not dependent on the caller. The method now is an independent agent (like a thread) that runs on its own. It has returned a Task that is a handle to itself. The caller can do with that task as he pleases (e.g. wait for it, await it, ...).
But if the caller simply drops that Task, the async methods keeps running.
The "re-enter" part of your picture happens at a time controlled by the awaited awaitable. Often, this is some external event such as a completed IO or a timer. The async method now resumes execution not knowing or caring who re-activated it.
Think of each async method as an independent thread. Each await logically is a Thread.Join().

How can async only work on one thread?

If some code 'await's and the single thread carries on running a program, how can the bit that's been paused come back to life? Does the single thread need to go back a poll the state of the paused bit of code to see if its finished?
Both the MSDN docs and my introductory async blog post answer this.
In summary, when an async method encounters an await for an operation that is not yet completed, then by default it will capture the current context. This "context" is the current SynchronizationContext, unless it is null, in which case it is the current TaskScheduler. Later, when the operation completes, the remainder of the async method is scheduled to that context.
In UI apps, this can be a UI SynchronizationContext (scheduling the method on the UI thread). In ASP.NET apps, this is usually a request context (which is not bound to a specific thread). If you're running an async method on the thread pool, it's usually the thread pool context (again, not bound to any specific thread).
Returning to the UI example, the UI thread does have a central "message loop" that it runs, processing Win32 messages from its queue. The UI SynchronizationContext simply posts a message to the queue telling the UI thread to execute the next part of the async method.
[...] how can the bit that's been paused come back to life?
By using a delegate (taken from msdn).
The “await” keyword tells the compiler to insert a possible
suspension/resumption point into a method marked as “async”.
Logically this means that when you write “await someObject;” the
compiler will generate code that checks whether the operation
represented by someObject has already completed. If it has, execution
continues synchronously over the await point. If it hasn’t, the
generated code will hook up a continuation delegate to the awaited
object such that when the represented operation completes, that
continuation delegate will be invoked. This continuation delegate will
re-enter the method, picking up at this await location where the
previous invocation left off. At this point, regardless of whether the
awaited object had already completed by the time it was awaited, any
result from the object will be extracted, or if the operation failed,
any exception that occurred will be propagated.
Does the single thread need to go back a poll the state of the paused bit of code to see if its finished?
No, there is no such thing. When you call await the control from the current thread goes one step up the call-stack, ie it is returned to the callee of the method that contains await. When the task is finished, TPL calls a delegate method, which resumes control from the await line.
I actually don't know specifically how await is implemented in C#, but let me propose a way it could be, just to answer the question "How is it possible?" (as opposed to "How is it implemented"?).
First consider the yield keyword, which is completely different but, I think, more generally understood. When you write a function that returns IEnumerable<T> and use yield return within the body of the function, most of us by now understand that the C# compiler generates an iterator class for you, and produces IL that looks very different from the code you wrote. My point being that we must realize the C# code itself does not always map very clearly to a set of IL instructions; sometimes the transformation is complex.
Such must also be the case with await, where the C# compiler doesn't simply emit a bunch of simple steps for each line of C# code. What it could do instead, for example, is take the call with await, generate a delegate that calls the given method, and then bundle up the rest of the method's code as a callback to the delegate.
How that other method operates asynchronously is immaterial to the await keyword. It might execute work on a separate thread, or perform some I/O operations. Whatever it does, it must conform to the asynchronous pattern of returning a Task<T>; this is what await relies on.
The resulting function, in IL, would then return, after having added the remaining logic as a listener to receive a signal when the work taking place on the separate thread is completed. That separate thread would then post a message back to the original thread (which, in most scenarios, will actually be a message loop), along with a reference to the listener.

What happens to the thread when reaching 'await' on 'async' method?

My question as the title suggest is about the background of 'async' and 'await'.
Is it true to say that what the current thread reaches 'await' keyword, it goes to "sleep",
and wakes up when the await method is done?
Thanks!
Guy
Is it true to say that what the current thread reaches 'await' keyword, it goes to "sleep", and wakes up when the await method is done?
No. The whole point of async is to avoid having threads sleeping when they could be doing other work. Additionally, if the thread running the async method is a UI thread, you really don't want it to be sleeping at all - you want it to be available to other events.
When execution reaches an await expression, the generated code will check whether the thing you're awaiting is already available. If it is, you can use it and keep going. Otherwise, it will add a continuation to the "awaitable" part, and return immediately.
The continuation makes sure that the rest of the async method gets run when the awaitable value is ready. Which thread that happens in depends on the context in which you're awaiting - if the async method is running in thread pool threads, the continuation could run on a different thread to the one the method started on... but that shouldn't matter. (The rest of the context will still be propagated.)
Note that it's fine for the async method to return without having completed - because an async method can't return a value directly - it always returns a Task<T> (or Task, or void)... and the task returned by the method will be only be completed when the async method has really reached the end.
async is only syntactic sugar that allows await keyword to be used.
If async, await is used in ASP.NET Core, then your request thread will be released to thread pool.
As Stephen Cleary says:
Asynchronous request handlers operate differently. When a request
comes in, ASP.NET takes one of its thread pool threads and assigns it
to that request. This time the request handler will call that external
resource asynchronously. This returns the request thread to the thread
pool until the call to the external resource returns. Figure 3
illustrates the thread pool with two threads while the request is
asynchronously waiting for the external resource.
The important difference is that the request thread has been returned
to the thread pool while the asynchronous call is in progress. While
the thread is in the thread pool, it’s no longer associated with that
request. This time, when the external resource call returns, ASP.NET
takes one of its thread pool threads and reassigns it to that request.
That thread continues processing the request. When the request is
completed, that thread is again returned to the thread pool. Note that
with synchronous handlers, the same thread is used for the lifetime of
the request; with asynchronous handlers, in contrast, different
threads may be assigned to the same request (at different times).
For desktop application:
await releases the current thread, but NOT to the thread pool.
The UI thread doesn't come from the thread pool. If you run asynchronous method,
e.g. ExecuteScalarAsync without async, await keywords, then this method
will run asynchronously no matter what. The calling thread won't be
affected .
Special thanks for nice comments to Panagiotis Kanavos.
E.g. you have a heavy stored procedure and your stored procedure takes 10 minutes to be executed. And if you run this code from C# without async, await keywords, then your execution thread will wait your stored procedure for 10 minutes. And this waiting thread will do nothing, it will just wait stored procedure.
However, if async, await keyword is used, then your thread will not wait stored procedure. The thread will be eligible to work.
Although this question has already been answered by Jon Skeet who is a highly skilled person (and one of my favorites), it is worth reading the contents that I mention below for other readers of this post.
By using an async keyword on a method, the original asynchronous method creates a state machine instance, initializes it with the captured state (including this pointer if the method is not static), and then starts the execution by calling AsyncTaskMethodBuilder<T>.Start with the state machine instance passed by reference.
As soon as control reaches an await keyword, the current thread (which can be a .Net thread pool's worker thread), creates a callback (as a delegate) to execute the rest of the sync code exactly after the await keyword (Continuation) using the SynchronizationContext/TaskSheduler's APIs (SynchronizationContext may not be present in all applications, such as Console Applications or ASP.Net Core Web Applications), the captured SynchronizationContext is stored in the state machine as an object, the IO work is sent to an IOCP thread, and the current thread is then released.
The IOCP thread binds to an IOCP (IO Completion Port), opens a connection, and asks it to execute the code that has been waited, and the IOCP sends the execution command to the corresponding device (socket/drive).
Whenever the IO work is finished by the relevant device, a signal from the IOCP is returned to the IOCP thread along with the result of the IO work, and then the IOCP thread, based on that captured SynchronizationContext determines which thread of thread pool should process the continuation/callback (that was stored in the state machine).
Also, the following articles can be useful:
https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/
https://tooslowexception.com/net-asyncawait-in-a-single-picture/
https://devblogs.microsoft.com/dotnet/configureawait-faq/#what-is-a-synchronizationcontext
No. Current thread actually doesn't go to sleep. The execution continues. This is the whole trick of it. You may have some code that processes data while asynchronous actions are still pending. It means that by the time those async completes your main thread is free to run and process other data.
As for the other part of the question - async just executes on another thread, not the current one. I believe that CLR is responsible for spinning those threads, so that many async actions are allowed at the same time (i.e. you may be retrieving data asynchronously from different web servers at the same time).

Async methods don't require additional threads?

In MSDN, there is a paragraph like this:
The async and await keywords don't cause additional threads to be
created. Async methods don't require multithreading because an async
method doesn't run on its own thread. The method runs on the current
synchronization context and uses time on the thread only when the
method is active. You can use Task.Run to move CPU-bound work to a
background thread, but a background thread doesn't help with a process
that's just waiting for results to become available.
But it looks I need little more help with the bold text since I am not sure what it exactly means. So how come it becomes async without using Threads?
Source: http://msdn.microsoft.com/en-us/library/hh191443.aspx
There are many asynchronous operations which don't require the use of multiple threads. Things like Asynchronous IO work by having interrupts which signal when data is available. This allows you to have an asynchronous call which isn't using extra threads - when the signal occurs, the operation completes.
Task.Run can be used to make your own CPU-based async methods, which will run on its own separate thread. The paragraph was intended to show that this isn't the only option, however.
async/await is not just about using more threads. It's about using the threads you have more effectively. When operations block, such as waiting on a download or file read, the async/await pattern allows you to use that existing thread for something else. The compiler handles all the magic plumbing underneath, making it much easier to develop with.
See http://msdn.microsoft.com/en-us/magazine/hh456401.aspx for the problem description and the whitepaper at http://www.microsoft.com/en-us/download/details.aspx?id=14058.
Not the code generated by the async and await keyword themselves, no. They create code that runs on your the current thread, assuming it has a synchronization context. If it doesn't then you actually do get threads, but that's using the pattern for no good reason. The await expression, what you write on the right side of the await keyword causes threads to run.
But that thread is often not observable, it may be a device driver thread. Which reports that it is done with a I/O completion port. Pretty common, I/O is always a good reason to use await. If not already forced on you by WinRT, the real reason that async/await got added.
A note about "having a synchronization context". You have one on a thread if the SynchronizationContext.Current property is not null. This is almost only ever the case on the main thread of a gui app. Also the only place where you normally ever worry about having delays not freeze your user interface.
Essentially what it's doing is when you run an async method without calling it with await is this:
Start the method and do as much as possible sychronously.
When necessary, pause the method and put the rest of it into a continuation.
When the async part is completed (is no longer being waited on), schedule the continuation to run on the same thread.
Whatever you want can run on this thread as normal. You can even examine/manipulate the Task returned from the async method.
When the thread becomes available, it will run the rest of your method.
The 'async part' could be file IO, a web request, or pretty much anything, as long as calling code can wait on this task to complete. This includes, but is not limited to, a separate thread. As Reed Copsey pointed out, there are other ways of performing async operations, like interrupts.

Categories