Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the logical call context not propagated across threads?

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.

like image 701
Lars Baunwall Avatar asked Dec 10 '22 19:12

Lars Baunwall


1 Answers

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.

like image 54
khellang Avatar answered Feb 02 '23 00:02

khellang