Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreadStatic for TPL Task

How can something like a ThreadStatic be used in a TPL Task? My understanding ("Wrox Professional Parallel Programming with C#", p74) is that a Task can switch from one thread to another during execution.

What I want to do?

I want to maintain a session id inside a static class so I don't need to pass this id to all of my methods. My library has methods like login(id), logout(id) and many methods which operate on credentials associated with this id. But I don't want to pass this id to every method. I can make sure my library is called within different thread for different sessions. So saving the id inside login() in a ThreadStatic variable will work.

Now I want to use TPL Tasks which are created for me by a ThreadPool. I can pass my session id to the Task, but if I store this id inside a ThreadStatic variable it will not survive if my Task switches threads.

like image 215
Gerard Avatar asked Aug 01 '11 09:08

Gerard


People also ask

What is TPL Task Parallel Library and how it differs from threads?

Compared to the classic threading model in . NET, Task Parallel Library minimizes the complexity of using threads and provides an abstraction through a set of APIs that help developers focus more on the application program instead of focusing on how the threads will be provisioned.

How TPL works?

The TPL scales the degree of concurrency dynamically to most efficiently use all the processors that are available. In addition, the TPL handles the partitioning of the work, the scheduling of threads on the ThreadPool, cancellation support, state management, and other low-level details.

Does Task use thread?

A new Thread()is not dealing with Thread pool thread, whereas Task does use thread pool thread. A Task is a higher level concept than Thread.

What is multithreading task?

The Thread class is used for creating and manipulating a thread in Windows. A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel.


1 Answers

TPL and .Net 4.5's async flow the ExecutionContext, which means you can use CallContext.LogicalSetData(string, object) and CallContext.GetLogicalData(string) in much the same way you would use ThreadStatic. It does incur a significant performance penalty, however. This has been exposed in .Net 4.6 and up (including .Net Standard 1.3 and up) with the AsyncLocal<> wrapper.

See Async Causality Chain Tracking, How to include own data in ExecutionContext, and ExecutionContext vs SynchronizationContext for a deeper dive.

Example use:

class Program
{
    static async void Main(string[] args)
    {
        Logger.Current = new Logger("Test Printer");

        Logger.Current.Print("hello from main");
        await Task.Run(() => Logger.Current.Print($"hello from thread {Thread.CurrentThread.ManagedThreadId}"));
        await Task.Run(() => Logger.Current.Print($"hello from thread {Thread.CurrentThread.ManagedThreadId}"));
    }
}

class Logger
{
    private string LogName;

    public Logger(string logName)
    {
        if (logName == null)
            throw new InvalidOperationException();

        this.LogName = logName;
    }

    public void Print(string text)
    {
        Console.WriteLine(LogName + ": " + text);
    }

    private static AsyncLocal<Logger> _logger = new AsyncLocal<Logger>();
    public static Logger Current
    {
        get => _logger.Value;
        set => _logger.Value = value;
        }
    }
}

Prints:

Test Printer: hello from main  
Test Printer: hello from thread 11 
Test Printer: hello from thread 10
like image 85
Mitch Avatar answered Sep 21 '22 05:09

Mitch