I'm using Thread to store Locale and pass it down the layers.
In my middleware I set the selected locale in the current thread as follow:
Thread.SetData(Thread.GetNamedDataSlot('SelectedLocale'), selectedLocale /* I get this value form the request */);
Then in the rest of my code, I use this line to access that data:
var selectedLocale = Thread.GetData(Thread.GetNamedDataSlot('SelectedLocale'));
However, this sometimes works, and sometimes returns null.
I used var threadId = Thread.CurrentThread.ManagedThreadId and realized that Id changes sometimes.
Why is it so? How can I make sure that I'm using the same thread during my request processing?
Update
I'm using the same technique for my APIs and for my Razor Page applications.
My APIs work just fine. They never fail.
However, my Razor Page applications fail almost 70% of the time. But they also work sometimes.
I think there must be something related to the Razor Pages here.
I'm using Thread to store Locale and pass it down the layers.
I'm not using async/await in my code
If you're still thinking in threads and not using async/await, you're doing it wrong. Threads are too low-level of a construct to be using in application code, especially web application code.
It's all tasks and contexts now. If you use Razor pages, stuff will be handled async by the framework. Just use the HttpContext to pass entities from one middleware to the next.
Though the HttpContext has its own problems.
More reading: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0, https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-6.0.
Related
I know you should only use async for stuff which is not "CPU-intensive", e.g. file writes, web calls etc. and therefore I also know it doesn't makes sense to wrap every method into Task.Run or something similar.
However what should I do when I know a method does a web call, but it doesn't offer an async interface. Is it in this case worth to wrap it?
Concrete example:
I'm using CSOM (Client SharePoint Object Model) in my WebApi application (server) and want to get a SharePoint list.
This is normally done like this:
[HttpGet]
[Route("foo/{webUrl}")]
public int GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
context.ExecuteQuery();
return web.Lists.Count;
}
}
And I thought about changing it to something like this:
[HttpGet]
[Route("foo/{webUrl}")]
public async Task<int> GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
await Task.Run(() => clientContext.ExecuteQuery());
return web.Lists.Count;
}
}
Does it make sense and does it help? As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
But is it worth it and should it be done like this?
If so:
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
edit:
updated to use Task.Run as suggested in comment.
However what should I do when I know a method does a web call, but it doesn't offer an async interface.
Unfortunately still somewhat common. As different libraries update their APIs, they will eventually catch up.
Is it in this case worth to wrap it?
Yes, if you're dealing with a UI thread. Otherwise, no.
Concrete example... in my WebApi application (server)
Then, no, you don't want to wrap in Task.Run. As noted in my article on async ASP.NET:
You can kick off some background work by awaiting Task.Run, but there’s no point in doing so. In fact, that will actually hurt your scalability by interfering with the ASP.NET thread pool heuristics... As a general rule, don’t queue work to the thread pool on ASP.NET.
Wrapping with Task.Run on ASP.NET:
Interferes with the ASP.NET thread pool heuristics twice (by taking a thread now and then releasing it later).
Adds overhead (code has to switch threads).
Does not free up a thread (the total number of threads used for this request is almost equal to just calling the synchronous version).
As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
Yes, but all you're doing is jumping threads, for no benefit. The thread used to block on the query result is one less thread ASP.NET has to use to handle requests, so freeing up one thread by consuming another isn't a good tradeoff.
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
Some of the "older" MS APIs just haven't gotten around to adding async versions yet. They certainly should, but developer time is a finite resource.
This is my personal view of your problem and for me the above way is not required. When we host your API in IIS, the server assigns one thread from thread pool it has in the server. The IIS also has a setting of maxConcurrentRequestsPerCPU maxConcurrentThreadsPerCPU. You can setup these values to serve the request instead of handling the request all by yourself.
Please consider these sceanrios:
An async .ashx handler
A async .asmx web-service method
A sync MVC 5 controller action method
I am trying to figure out a way to set "logical thread" specific data that can be accessed consistently during a "logical" http request, i.e. if the data was set on the thread in the "BeginExecute" part of which-ever async handler you would consider, that data is available in the "EndExecute" part of that asnc handler even if ASP.NET executes the "EndExecute" part on a different OS/.Net thread.
Moreover, I am expecting that the data set in the "BeginExecute" part on whatever OS/.Net thread it was on is NOT available on a subsequent http request if the second request is assigned the thread that was earlier assigned to first http request when it was in "BeginExecute" portion but this thread freed up as the first http request went in its async operation (and its possibly still completing its async operation).
I believe the word "logical thread" or "logical thread context" in .Net actually means the same "logical" flow of operation that I have mentioned (and not the underlying OS/.Net thread that keeps getting re-assigned). If you look at it from a workflow perspective, each http request is a new "logical" operation (even if multiple users invoke the same web-service sequentially or in parallel, each request is a new and separte logical operation), and in this meaning, the "logical" operation is one-time and cannot repeat. However the same underlying OS/.Net threads can be mapped to "logical" operations as they arrive based on their availability.
Additionally I want to expose this data as HttpContext.Current sort of static property. To some people this may come as a surprise, but HttpContext.Current does not work correctly if you are using for example async .asmx web-service methods. I am sure I have read content on web which says HttpContext.Current should always return correct HttpContext, but I have seen it as null in EndExecuteMethod of .asmx web-methods. It would be great if somecan can confirm if I am right in making my last statement, but this statement is not the overall question I am trying to ask here.
After reading a good amount of literature (e.g. What is the difference between log4net.ThreadContext and log4net.LogicalThreadContext?, http://msmvps.com/blogs/jon_skeet/archive/2010/11/08/the-importance-of-context-and-a-question-of-explicitness.aspx, http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html and more including MSDN docs), here are my inferences:
ThreadStatic is local to underlying OS/.Net thread and not to the "logical" operation, hence in my example; data set on first http request in "BeginExecute" would be visible in next http request if the second http request gets assigned the same thread as "BeginExecute" for first thread. And this data won't be available in "EndExecute" if it happens to be re-assigned to another thread by .Net (which would happen in vast majority of the cases).
Thread.SetData is even more problematic for my use-case. It needs data slots to be passed in and if I were to pass in a data slot from a return value of Thread.GetNamedDataSlot, the information is available across the app domain; as named data slots are shared between threads.
CallContext.SetData is like ThreadStatic (which means its not shared by app domain but different http requests would see the same data if they get assgined to the same underlying OS/.Net thread). CallContext.SetData provides an additional ability to marshal the context data for RPC calls which is irrelevant to the current question being asked.
Then there's the ThreadLocal class (.Net 4/.Net 4.5). It could have solved one part of my problem it seems, I could have passed it inside stateObject of BeingExecute operation, and extract from the same stateObject parameter of endExecute operation. From this perspective, ThreadLocal seems to be written for .Net's async support. But it won't work when I need to access it like HttpContext.Current as there's no way I can see to preserve the "logical thread static" instance ofit (unless I have said something incorrectly in my previous 3 points).
And finally it seems CallContext.LogicalSetData does what I intend to achive. Using the set of CallContext.LogicalSetData and CallContext.LogicalGetData methods, I should be able to achieve the HttpContext.Current like impact which works correctly for "logical task executions".
Now come the questions:
Is everything I have said above correct. Please correct any and all incorrect claims I have made.
Are there any other options available for thread static kind of feature in .Net that I missed.
Does CallContext.LogicalSetData/LogicalGetData pass on the context data to RPC calls (the msdn page does not mention clearly, http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.logicalsetdata(v=vs.110).aspx).
Are there any downsides (performance wise or otherwise) of using CallContext.LogicalSetData/LogicalGetData.
This page says something about copy-on-write behavior for LogicalSetData: http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html. In the context of async handlers/async MVC 5 action methods, what's the impact if I save a reference type using logicalsetdata and later change the state of the reference type. What are the repuccursions.
For mutation/logicalsetdata/async, I still can't see what's the problem by mutating the object. When the async method starts, the copy-on-write behavior would trigger a copy of context data the next time logicalsetdata is called. This is a shallow copy, so my reference object is now actually shared by 2 logical contexts and the changes in one context are visible in the other context which is what I would normally expect from a reference type.
A long question with lots of references, but hopefully I did my research well and the answers would benefit other people too.
I am trying to figure out a way to set "logical thread" specific data that can be accessed consistently during a "logical" http request
The only possible options are HttpContext.Current.Items and the logical CallContext.
Moreover, I am expecting that the data set in the "BeginExecute" part on whatever OS/.Net thread it was on is NOT available on a subsequent http request
HttpContext.Current.Items will always be cleared on a new request, but you'll have to clear the logical CallContext data yourself.
HttpContext.Current does not work correctly if you are using for example async .asmx web-service methods.
I find this surprising. I haven't tried it, but it should work - if you are running on .NET 4.5, targeting .NET 4.5 (i.e., have targetFramework set to 4.5 in your web.config), and aren't using async void.
[ThreadStatic], thread-local data slots, (non-logical) CallContext, and ThreadLocal are all thread-specific data, and will not work for asynchronous code.
Is everything I have said above correct. Please correct any and all incorrect claims I have made.
There is really way too much text in your question. Stack Overflow is a Q&A site, not a mentoring site.
Are there any other options available for thread static kind of feature in .Net that I missed.
No.
Does CallContext.LogicalSetData/LogicalGetData pass on the context data to RPC calls
I have no idea. Try it and see.
Are there any downsides (performance wise or otherwise) of using CallContext.LogicalSetData/LogicalGetData.
There's a definite performance hit. The .NET framework is highly optimized for the common case (no logical call context data).
what's the impact if I save a reference type using logicalsetdata and later change the state of the reference type.
The logical CallContext has shallow-copy-on-write behavior. So, any kind of asynchronous fork/join concurrency (i.e., Task.WhenAll) will end up sharing that state. If you use ConfigureAwait(false), you could also end up with race conditions.
To actually solve your problem, I recommend you first look into why HttpContext.Current doesn't work as expected; my guess (without seeing the project) is that targetFramework is set to 4.0 instead of 4.5. HttpContext.Current.Items is the most performant choice if you can get it working.
I've not dealt much with Async/threads/Tasks other than some web services.
I'm using MVC4. I have existing code which takes some time to run. It is using an existing method in the service layer, which uses various other the areas in further layers.
Essentially I was hoping to be able to make an ASync call from the Asynccontroller to that method. However it appears that I would need to change/create another method to implement all the Task & await keywords, quite a hefty job altering all the way down the chain.
Is it possible to call/'fire' a synchronous method in this manner?
I want the long process (creating some documents in the background) to continue running even if the user closes their browser. However if the user still has the browser open then I would like to return a notification to them.
Is there a better way to fire a background task to execute from the MVC Application?
I think you're trying to use async for something it cannot do. As I describe on my blog, async does not change the HTTP protocol.
Is it possible to call/'fire' a synchronous method in this manner?
Sort of. You can use Task.Run if you have CPU-bound work that you want to move off the UI thread in a desktop/mobile application. But there is no point in doing that in an ASP.NET MVC application.
I want the long process (creating some documents in the background) to continue running even if the user closes their browser. However if the user still has the browser open then I would like to return a notification to them.
The problem with this is that you'd be returning early from an ASP.NET request, and (as I describe on my blog), that's quite dangerous.
A proper solution would be to queue the work in a reliable queue (e.g., Azure queue or MSMQ), have an independent backend for processing (e.g., Azure worker role / web job or Win32 service), and use something like SignalR for notification.
As soon as you attempt to do work in an ASP.NET process without a request context, then you run into the danger that your process may exit without completing the work. If you are OK with this, then you can use the BackgroundTaskManager type from my blog above to minimize the chance of that happening (but keep in mind: it can still happen).
I hope you can help me.
I am doing some webrequests within my C# 4.0 application which require authentication. I simply use the CredentialsCache.DefaultCredentials. This works great as long as I do not run the functionality in a different thread / task via Task<T>.Factory.StartNew(...). I then get 401 errors. I assume that the credentials are not passed through to child threads?
How can I pass through the credentials to any child tasks / threads?
I assume that you are using impersonation and that the problem is that the credentials are not being flowed into the Task. It's worth checking this point to save a wild goose chase by dumping out the value of Windows.Identity.GetCurrent().Name in both the initiating code and the Task body and making sure it's what you expect.
So given the above, there are a couple of ways that (to put it formally) execution context (or just security context) is not being flowed across threads. The default behaviour is that context is flowed - so something must be affecting that.
1) Something has set ExecutionContext.SuppressFlow() - you can check this by dumping the value of ExecutionContext.IsFlowSuppressed() inside the task.
2) There is a configuration element <legacyImpersonationPolicy> (conifguration->runtime->legacyImpersonationPolicy) that defaults to false. When false, WindowsIdentity is flowed across asychronous points. When true it is not. This is regardless of the ExecutionContext flow settings. So true here would cause a problem for you. You can check this by dumping the value of SecurityContext.IsWindowsIdentityFlowSuppressed() is your task. This can also be programmatically set per thread with SecurityContext.SuppressFlowWindowsIdentity().
Finally, for completeness, in case you are using unmanaged code there is another setting <alwaysFlowImpersonationPolicy> that controls how impersonated credentials are flowed in unmanaged scenarios as well; the other settings described only affect managed code.
In case someone else comes across this issue... mine was a little different. My WCF service is exposed as a REST service as well as a SOAP, with the security context coming from either System.Web.HttpContext.Current or System.ServiceModel.OperationContext.Current. My DAL uses one of those contexts to identify the current user by checking HttpContext.Current.User.Identity or OperationContext.Current.ClaimsPrincipal.Identity.
So for my case, I had to save those two contexts in variables outside of the parallel foreach and then set them inside the parallel to the saved values. That seemed to do the trick. The same concept could work for other TPL situations with some modifications.
var httpcontext = System.Web.HttpContext.Current;
var opcontext = System.ServiceModel.OperationContext.Current;
Parallel.ForEach(types, (p) =>
{
System.Web.HttpContext.Current = httpcontext;
System.ServiceModel.OperationContext.Current = opcontext;
// DO YOUR PARALLEL PROCESSING HERE
});
What are differences between CallContext and ThreadStatic?
I've understood that in an ASP.NET environment data stored in CallContext could be persisted throughout the request until it ends while ThreadStatic may or may not work since the request may switch threads. I've also learned that the HttpContext is internally stored using the CallContext.
In a regular application they both seem to persist throughout the same thread call. When isn't this the case?
Edit: In the comments I learned that the call context is an abstraction over a thread static store. The ASP.NET framework explicitly moves the data from one thread to the next that is to handle one request. Other framework that wants to provide thread agility could do the same to for contextual storage.
Very often a request will use the same thread throughout, but it certainly won't always be the case - ASP.NET exhibits thread agility. There's an old in-depth blog article about the matter from 2005, but as of .NET 4.5 things are rather better.
Items stored as ThreadStatic are available to more than one request. IIS reuses the thread after a request is complete to process subsequent requests, it can even swap a request from one thread to another during processing. ASP.Net clears down the CallContext after each request.