Thanks to Reed Copsey for the nice article and Theodor Zoulias for reminding about ThreadLocal<T>
.
The new ThreadLocal class provides us with a strongly typed,
locally scoped object we can use to setup data that is kept separate
for each thread. This allows us to use data stored per thread,
without having to introduce static variables into our types.
Internally, the ThreadLocal instance will automatically setup the
static data, manage its lifetime, do all of the casting to and from
our specific type. This makes developing much simpler.
A msdn example:
// Demonstrates:
// ThreadLocal(T) constructor
// ThreadLocal(T).Value
// One usage of ThreadLocal(T)
static void Main()
{
// Thread-Local variable that yields a name for a thread
ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
{
return "Thread" + Thread.CurrentThread.ManagedThreadId;
});
// Action that prints out ThreadName for the current thread
Action action = () =>
{
// If ThreadName.IsValueCreated is true, it means that we are not the
// first action to run on this thread.
bool repeat = ThreadName.IsValueCreated;
Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
};
// Launch eight of them. On 4 cores or less, you should see some repeat ThreadNames
Parallel.Invoke(action, action, action, action, action, action, action, action);
// Dispose when you are done
ThreadName.Dispose();
}
So your code would like this:
ThreadLocal<Random> ThreadName = new ThreadLocal<Random>(() =>
{
return new Random();
});
var options = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 4,
EnsureOrdered = false,
BoundedCapacity = 4 * 8
};
var actionBlock = new ActionBlock<int>(async request =>
{
bool repeat = ThreadName.IsValueCreated;
var random = ThreadName.Value; // your local random class
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId},
repeat: ", repeat ? "(repeat)" : "");
}, options);