HTTPContext across threads

12,464

Solution 1

ASP.NET may migrate request between threads if it's under load. Once request is received page constructor may execute on one thread and page load on another. In this thread switch CallContext and ThreadStatic are not migrated, but luckaly HttpContext is.

This may be misleading as HttpContext is call context, but this is a little quirk in ASP.NET, probably due to cutting corners to improve performance.

You'll have to remove dependencies to CallContext and use HttpContext entire way through.

You can read more details in this terrific blog post by Piers7.

Solution 2

This was resolved during a chat session.

In essence it involves long-running tasks and a suggestion of using an external service (Web, or regular Windows Service) was decided as the best solution to the problem.

Share:
12,464
Kamyar Nazeri
Author by

Kamyar Nazeri

Coder for life with expertise in C++, C#, Python, JavaScript, TypeScript, and most recently with a focus on Cloud, Deep Learning in Computer Vision, and Generative Models. Homepage: knazeri.github.io Twitter: @knazeri LinkedIn: kamyar nazeri GitHub: github.com/knazeri

Updated on June 04, 2022

Comments

  • Kamyar Nazeri
    Kamyar Nazeri almost 2 years

    I need to instantiate a singleton object per web request, so that the data is processed once and is valid throughout the request, I was using HttpContext.Current.Items to share data during HTTP request, everything was fine until we needed the singleton object instance across multiple threads, the first thing that I came up with was to pass the HttpContext instance to the new thread:

    HttpContext context = HttpContext.Current;
    ThreadPool.QueueUserWorkItem(callback =>
        {
            HttpContext.Current = context;
            // blah blah
        });
    

    Which I don't think is a thread-safe approach as noted here.

    Using Reflector I figured HttpContext.Current.Items actually uses CallContext to store objects in each logical thread. So I changed the singleton interface to this:

    public static SingletonType SingletonInstance
    {
        get { return CallContext.GetData(key) as SingletonType; }
        set { CallContext.SetData(key, value); }
    }
    

    And simply overwrite SingletonInstance when starting any new thread! The code works fine, however it seems that somehow under heavy load, CallContext.GetData(key) returns null and the application crashes with with a null reference exception!

    I was thinking, if CallContext.GetData is atomic? But it just doesn't seem right, the CallContext is thread specific data storage and must be atomic or I am missing the point!

    My other guess is that setting the SingletonInstance (CallContext.SetData) happens in one thread while CallContext.GetData executes in another as noted here but I don't know how/why?

    update:

    We are keeping an instance of each online user in an array on the server. The singleton object is actually a reference to the object representing current user. Current user must be unique and available in each thread for database querying, logging, error handling and more, this is how it is done:

    public static ApplicationUser CurrentUser
    {
        get { return CallContext.GetData("ApplicationUser") as ApplicationUser ; }
        set { CallContext.SetData("ApplicationUser", value); }
    }