Understanding context in .Net Task execution - c#

I have been trying to understand the concept of context in Task execution in .Net. However, I still am unable to relate context to basic concept of OS threads. While going through this blog, I sort of got the following idea of what a context is:
In GUI applications, there is a single GUI thread which has all the
GUI elements. Now, because one needs to come in the GUI thread to
access the GUI elements, I am assuming the GUI thread has the GUI
elements initialized in its stack space which it doesn't share with
other threads. Therefore, the awaitable needs to schedule the
remainder of the function in the GUI thread if the remainder function
wants to access some GUI element. Similarly, if we talk about HTTP
application which accept HTTP get/post requests, there is a thread
which gets spawned when a request arrives. This thread contains the request context like IP address of the user. Now, if the remainder
function wants to access some HTTP context properties, it has to execute in that thread.
While reading this blog, I came across the idea of context being copied. This has led to me believe the following:
The context of a thread is data members like IP address, GUI elements
etc. When the remainder of a function is scheduled after an awaitable
completes, the remainder may need the context to be present, but not
necessarily on the same thread. So, what is done is any thread is
taken out of the thread pool and the context is copied onto that
thread so that it is accessible. Thereafter, the remainder function is
scheduled on this thread. This can cause a deadlock in the following way. Take GUI
applications for example. At any time, there should be a unique thread
having the GUI context. So, if the GUI thread blocks and doesn't
release the context, the remainder function won't get scheduled.
Can anyone please clarify this for me? What exactly is in a context? And how is the context transferred? Which of the above understanding of mine is right or both are wrong?
UPDATE:
I read this blog and it has a line And this extension method demonstrates how to invoke a function with a specified ExecutionContext (typically, captured from another thread). This is pushing me to believe that my second idea is closer to correctness.

Each context is different. But in general, they're not copied. Contexts are used to schedule Tasks. That is, to find a suitable thread and other resources, as required, and to then execute the task.
In some contexts (GUI), the most important thing is the thread. There's one UI thread, so any Task that a GUI context is asked to schedule has to arrange for the UI thread to execute that Task.
In some contexts (ASP.Net before core), what's important are the "ambient" request/response/session objects. Those objects should only be accessed by a single thread at a time, but any thread can be used. So the context can use thread pool threads but needs to ensure it only executes a single Task at a time.
And in the default context, there's no special thread nor any other special resources. Like the ASP.Net context above, any thread pool thread can be used to execute the Task but it can schedule Tasks as quickly as the thread pool will take them.

Related

C# Task ConfigureAwait

Thought I was getting a handle on ConfigureAwait, then I tried an experiment.
My understanding is that ConfigureAwait(false) will only make a difference if there is a synchronization context.
ASP, WPF, etc. should have a context, but console apps and service apps should not.
To see how it works I made a Web API app and included the following method:
// GET api/values/5
public async Task<string> Get (int id)
{
var syncCtx = SynchronizationContext.Current;
int startThreadId = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(true);
int endThreadId = Thread.CurrentThread.ManagedThreadId;
return "Start Thread ID: " + startThreadId.ToString() +
": End Thread ID: " + endThreadId.ToString();
}
My prediction was that with no ConfigureAwait or ConfigureAwait set to true, I should see the same thread ID before and after the await.
My first few tests showed exactly that with the true set as above.
Later runs of the code started and ended on different thread ids regardless of ConfigureAwait.
I added syncCtx to convince myself I have a context.
The one caveat I have read is that if the task has completed, you won't be guaranteed the same ID. Is that the case here? If so, why is that the case?
Have I set up a naive or flawed test? If so, what would be a proper test?
I started down this path in a console/service app and realized I was not getting the same thread ID. I was adding ConfigureAwait(false) as recommended in most "best practice" write ups I have seen. Since I like to see how things really work, I tried testing the Thread IDs. Seeing them differ led me through a number of searches that resulted in the above code.
Doing ConfigureAwait(true) (or just leaving it off, as it is the default value) does not make it run on the same thread. It tells SynchronizationContext.Current to do a Post or Send with the rest of the continueation. How Post or Send actually runs that code is not defined.
WindowsFormsSynchronizationContext and DispatcherSynchronizationContext (WinForms and WPF's contexts) both will put the continuation on to the queue of messages to be processed by the Message Pump (the UI thread).
On the other hand, AspNetSynchronizationContext (which is what you are running under) just sets some state information up (like setting HttpContext.Current back to its old value), it then queues the work on the next available thread pool thread. There is no "Message Pump" for ASP.NET, it just does all of its work on the thread pool, so there is no point in trying to get the same thread out of the thread pool at a later time, that thread may already been destroyed by the time the continuation happens.
The times you saw the same ID before and after you just got lucky and the same thread got pulled out of the thread pool before and after the continuation.
I highly recommend you read the MSDN magazine article "It's All About the SynchronizationContext", it goes in to detail explaining how the SynchronizationContext works and goes in to a little detail about the 4 types of contexts built in to .NET.
SynchronizationContext != Thread. If you were using something like WPF with its Dispatcher runtime then, yes, its SynchronizationContext happens to be tied to one specific thread. However things like ASP.NET and WCF are not thread affine, they just carry along with them specific ambient context that needs to be restored on whatever thread they begin executing on next. They may be affine to a specific thread pool, but, again, not a specific thread.
This is precisely why the SynchronizationContext abstraction was introduced: it allows different frameworks to decide exactly what affinity means to them and allows the async/await infrastructure to work on top of them in a totally agnostic way.

C#, how to run a function in the main thread when the main thread becomes idle

I'm trying to port a Dart application to C#, in my dart app I use Futures to queue up functions asynchronously such that the queue will execute each subsequent function at some point in the future when it's turn comes in the VM's asynchronous call queue but importantly these are run in the "root thread". I understand this is highly specific to the Dart VM, but I was wondering whether anything similar could be done in C#. I basically want to be able to pass a function which takes no arguments to some method or object which will call that function in the main thread when the main thread finds it has no more work to do. I should also say that my code is for a service, not a UI application.
You can do that by creating your own custom SynchronizationContext, Stephen Toub has an article on how to do that here. The SynchronizationContext is what is used to schedule work to be done. So by rolling your own, you need to keep a queue of the work to be done and then execute each action in the queue.
You can customize this so that when the queue is empty (i.e. the SynchronizationContext is idle) you execute the action that you need. More info on contexts here.
An effective way to send something to be executed to a particular SyncrhonizationContext is to use Task.Run. The code below will schedule your code to run in the current SC. Using Task.Run without specifying a TaskScheduler will schedule the work on a thread pool thread.
Task.Run( () =>/* you code */, CancellationToken.None, TaskScheduler.FromCurrentSynchronizationContext());

How to get the reference of TPL task's thread in C#?

When I create a task as
Task task = Task.Factory.StartNew(() => someMethod(args));
in C# 4.0+, how can I get the reference of the thread(s) of this task?
Is it possible that the task is executed in the same thread that created the task or spawn more than one thread?
Update:
The reasons are:
I'd like to identify the task's thread in debugger (and attribute a name for it), etc.
Is created task executed always in separate thread from the one in which a task was created?
Is it one, zero or more than one thread?
Is it executed on a single and the same core?
It is important to know since, for example, I can put to sleep the main thread thinking that I am freezing the background worker
Update:
Useful answer:
Specifying a Thread's Name when using Task.StartNew
Is created task executed always in separate thread from the one in which a task was created?
No, there are certain situations in which the TPL is able to determine that the task can be executed on the same thread that created it, either because the relevant task creation option (or task scheduler) was supplied, or as an optimization because the calling thread would otherwise not have anything to do. You don't really need to worry about this though; it's not like you're going to end up blocking the UI thread because the TPL choose to execute it's code in that context. That won't happen unless you specifically indicate that it should. For all intents and purposes you can assume that this never happens (unless you force it to happen) but behind the scenes, without you ever needing to realize it, yes, it can happen.
Is it one, zero or more than one thread?
By default, tasks are executed in the thread pool. The thread pool will vary in the number of threads it contains based on the workload it's given. It will start out at one, but grow if there is sufficient need, and shrink if that need disappears. If you specify the LongRunning option, a new thread will be created just for that Task. If you specify a custom TaskScheduler, you can have it do whatever you want it to.
Is it executed on a single and the same core?
Potentially, but not assuredly.
It is important to know since, for example, I can put to sleep the main thread thinking that I am freezing the background worker
Putting the main thread to sleep will not prevent background workers from working. That's the whole point of creating the background workers, the two tasks don't stop each other from doing work. Note that if the background workers ever try to access the UI either to report progress or display results, and the UI is blocked, then they will be waiting for the UI thread to be free at that point.
You can use:
System.Threading.Thread.CurrentThread
But as said in the comments, you use the TPL to abstract threading away, so going back to this "low level" is a likely indicator of poor design.
Task.Factory.StartNew() queues the task for execution (see here). The actual thread that executes the task and when it gets executed is up to the TaskScheduler specified (the current TaskScheduler is used if none is specified).
In .Net 4 the default TaskScheduler uses the ThreadPool to execute tasks (see here) so if a ThreadPool Thread queued the task the same thread can possibly execute it later on.
The number of threads is dictated by the ThreadPool.
You shouldn't really care about which core your tasks are executed on.
Queuing a Task for execution will most likely schedule it to be executed on a ThreadPool Thread so you won't be at risk of accidentally putting the main thread to sleep

Async/Await + FromEvent method

Based on the following question:
General purpose FromEvent method
How do I know which thread in my application the event will return?
I can somehow specify which thread will it continue?
What happens to the thread that is using this feature?
These responses appear to be obvious when I use WPF (Dispatcher/Main/UI Thread), but if I'm working with threads MTA, STA, Reactive, ThreadPool (Task/BackgroundWorker), how can I predict what will happen?
Is there any real benefit than using task.Wait() (if I do not have to worry about locking thread)?
How do I know which thread in my application the event will return?
You don't. You never do with events, unless the documentation for a specific event specifies the that it will be executed from the UI thread, a thread pool thread, etc.
I can somehow specify which thread will it continue?
If you want to run code in a UI thread then marshal to the UI thread in the event handler. If you want to run code in a thread pool thread then add a new task to the thread pool inside of the handler. Both of those tasks add overhead if not needed, so it's usually best to look at the documentation of the event to see which is needed.
However, in the case of the linked question, the whole idea is that you're no longer dealing with an event and an event handler, you're dealing with a Task. So if you add a continuation to the task, the question is where will that continuation run? That is entirely specified by you. You can use the default task scheduler and have it run in the thread pool, you can pass a UI SynchronizationContext to run in the UI thread, or you can just let it run wherever the task you are continuing runs. (Meaning you have no idea what thread will be running it.)
If you're using the task with await, then it will automatically configure the continuation to run in the synchronization context you were in before you started that async operation, which may or may not be the UI thread (but likely is). If you specifically don't want that, then use .ConfigureAwait(false);.
Is there any real benefit than using task.Wait() (if I do not have to worry about locking thread)?
The reason to use an asynchronous task based approach is that you're not blocking threads, particularly thread pool threads (since you've specifically said you're not blocking a UI, which is much worse). Having a thread sitting around doing nothing is a problem, in some environments more than others (such as ASP for a highly active site). By not doing a blocking wait, you aren't consuming those resources.
If you await a Task, then there is a "context" that is captured and used to resume the async method. This "context" is the current SynchronizationContext, unless it is null, in which case it's the current TaskScheduler (which these days is usually the thread pool scheduler).
If you're doing async programming, you should be using await and not Wait. Wait can cause deadlocks, as I explain on my blog.
You may also find my async/await intro helpful.
Using the technique you linked to you cannot predict the thread that this runs on. It might be the thread raising the event, but that is not guaranteed (no, really! It isn't. This is a common misbelief).
So you need to force a switch to whatever thread you want to run on. For example use Task.Run to switch to the thread pool or use TaskScheduler.FromCurrentSynchronizationContext to run on the UI.
If you await the task you are guaranteed to resume in the synchronization context that was set before the await. This is probably what you want.

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).

Categories