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.
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With