.Net core & SynchronizationContext & Thread.SetData
Asked Answered
E

2

11

From what I know, AspNetCore doesn't have SynchronizationContext .

That “re-entering” the request context involves a number of housekeeping tasks, such as setting HttpContext.Current and the current thread’s identity and culture.

So I've created a simple .Net Core Api project with an action:

    [HttpGet]
    [Route("checkVar")]
    public async Task<IActionResult> checkVar()
    {
        Thread.SetData(Thread.GetNamedDataSlot("Random"),4);
        await Task.Delay(1000);
        var res = Thread.GetData(Thread.GetNamedDataSlot("Random"));
    }

To my suruprise , res had a value of 4. I was surprised because I believe that SetData was part of the synchronization context. (which should not exist in asp.net core)

More, when I used ConfigureAwait(false) , I got null in res.

So now I'm confused. Because ConfigureAwait shouldn't have effect in asp.net core

Question:

If asp.net core doesn't have a SynchronizationContext, then why did 4 was available after await ? why does the ConfigureAwait change the result in a non-SynchronizationContext environment?

Eohippus answered 29/7, 2019 at 20:17 Comment(0)
S
18

I was surprised because I believe that SetData was part of the synchronization context. (which should not exist in asp.net core)

No; SetData is thread-local storage (TLS). So it's tied to a specific thread. This doesn't have anything to do with synchronization contexts.

More, when I used ConfigureAwait(false) , I got null in res.

Depending on when you run this code, how busy the server is, etc., you could get null or 4 with or without ConfigureAwait(false).

If asp.net core doesn't have a SynchronizationContext, then why did 4 was available after await ?

It's a thread-specific value. There's no SynchronizationContext on ASP.NET Core, and your code will resume on any available thread pool thread. If that thread happens to be the same thread that started that method, then the TLS is going to still be there because it's for that specific thread.

The same behavior actually applies to ASP.NET pre-Core. In that case, there is a SynchronizationContext, but that context isn't tied to any particular thread. Just like ASP.NET Core, asynchronous methods on ASP.NET pre-Core can resume on any available thread pool thread, so TLS data may or may not be there after an await.

To support this theory with data, try logging Environment.CurrentManagedThreadId before and after the await and see if there's any correlation between the data being present and the id remaining the same.

Said answered 30/7, 2019 at 2:30 Comment(8)
Isn't synchronization context contains culturinfo data etc? That's is the reason why ithought it also contains tlsEohippus
@RoyiNamir: The ASP.NET (pre-Core) SyncCtx includes the culture for the current request/page. That's the only SyncCtx that ever included that; none of the others do. In addition to the culture in the ASP.NET (pre-Core) SyncCtx, each thread also has its own culture and "UI culture".Said
@StephenCleary Hi Stephen, Thanks for the great Article. So when the async operation is completed and next line of code is getting executed, how does the request details like cookie/query string are preserved ? I mean without the SynchronizationContext, how does the .net core knows that which was the original thread that should continue the execution once async operation is performed ? Because I believe only that thread will have the details of that specific request, or how else the other thread will get the details of that specific request ?Noisy
@JayShah: The old ASP.NET way of doing this was setting they static HttpContext.Current property. That property does not exist on ASP.NET Core. With Core, all cookies, query strings, and other request/response-specific data is all local variables, not static variables.Said
Hi @StephenCleary, from the comment above, what do you mean by local variables? If the SyncCtx doesn't exist anymore, how's the HttpContext.Current property restored on (if happens) a different thread? Thanks.Thuythuya
@YuriCardoso: There is no HttpContext.Current property on ASP.NET Core. Local variables are ones defined in the method, as well as the implicit this.Said
@StephenCleary. How does ASP NET CORE know to resolve the same instance scoped per http request (or azure function execution) - even in the case where the thread executing the continuation is different from the initial thread.There must be some 'synchronization' mechanism: Quote: "The scoped service lifetime matches a function execution lifetime. Scoped services are created once per execution. Later requests for that service during the execution reuse the existing service instance." learn.microsoft.com/en-us/azure/azure-functions/…Formyl
@AdrianNasui: There is an AsyncLocal<T> type that provides an async-aware scope. It's roughly equivalent to the old logical call context. I suspect (but have not verified) that .NET Core DI uses this technique to determine the "current" scope. I do know that .NET Core logging scopes do use this technique.Said
P
1

You're invoking SetData on Thread. How come you thought it was part of SynchronizationContext.

You can easily test if there's a current SynchronizationContext by checking the value of SynchronizationContext.Current- If it's null, then there's no SynchronizationContext.

How many concurrent requests did you issued to test that code?

SynchronizationContext or not the way the framework/runtime flows the context (like in types like AsyncLocal<T>) is through the ExecutionContext data, not Thread data.

Permenter answered 29/7, 2019 at 23:29 Comment(2)
Isn't synchronization context contains culturinfo data etc? That's is the reason why ithought it also contains tlsEohippus
Where did you get that idea? The SynchronizationContext is only about synchronizing to a common context.Permenter

© 2022 - 2024 — McMap. All rights reserved.