I have a weird problem with logical call contexts not propagating to threads in my application.
In this sample, the thread (created before logical call context data is set) does not pick up the new value:
class Program
{
static void Main(string[] args)
{
var dispatcher = new MessageDispatcher();
//Logical call context data not set yet
Console.WriteLine($"Main: {Thread.CurrentThread.ManagedThreadId}: {CallContext.LogicalGetData("myvar")}"); // Main logical call context data is not set yet = Null
dispatcher.Broadcast("a string"); // logical call context data is still not set yet = Null
//Logical call context data is set now
CallContext.LogicalSetData("myvar", "Logical call context variable");
Console.WriteLine($"Main: {Thread.CurrentThread.ManagedThreadId}: {CallContext.LogicalGetData("myvar")}"); // Main logical call context data is set = return value
dispatcher.Broadcast("a string"); // Logical call context data is set and should return the data set above?
Console.ReadLine();
}
public class MessageDispatcher
{
private readonly ConcurrentQueue<string> _messages;
private readonly AutoResetEvent _waitHandle;
private bool _terminate;
private bool _messageMonitoringActive;
public MessageDispatcher()
{
_messages = new ConcurrentQueue<string>();
_waitHandle = new AutoResetEvent(false);
var thread = new Thread(MonitorCollection);
thread.Start();
}
public void Broadcast(string message)
{
_messages.Enqueue(message);
_waitHandle.Set();
}
private void MonitorCollection()
{
_waitHandle.WaitOne();
string message;
while (_messages.TryDequeue(out message))
{
Console.WriteLine(
$"{message}: {Thread.CurrentThread.ManagedThreadId}: {CallContext.LogicalGetData("myvar")}");
}
}
}
}
For all calls made within the created thread, the call context data is not adopted from the just set context variable. Why is that?
In my application, I can't set logical call context variable at instantiation (which is done via IoC on startup (it's a WebAPI stack)), but only on each invocation.
The MSDN documentation states that
CallContext is a specialized collection object similar to a Thread Local Storage for method calls and provides data slots that are unique to each logical thread of execution. The slots are not shared across call contexts on other logical threads. Objects can be added to the CallContext as it travels down and back up the execution code path, and examined by various objects along the path.
And also that
All methods in CallContext are static and operate on the call context in the current Thread.
Just from reading the documentation, you'd think that this scenario just won't work. And that's true for the SetData
and GetData
methods; if you use SetData
on one thread, you have to use GetData
on the same thread to get the data back out.
There is an exception, though; SetLogicalData
and GetLogicalData
, which you're already using. These are designed to flow context through "logical threads", i.e. across the same thread and through to child threads.
The problem with your approach is that you're trying to pass data after the child thread has been created and started, which won't work. If you move
CallContext.LogicalSetData("myvar", "Logical call context variable");
above
var dispatcher = new MessageDispatcher();
i.e. before the thread is created and started (in the MessageDispatcher
constructor). You'll see that you get the value out correctly.
In other words; you need to make sure all data is set before you create and start the child thread.
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