为什么逻辑调用上下文不跨线程传播? [英] Why is the logical call context not propagated across threads?

查看:69
本文介绍了为什么逻辑调用上下文不跨线程传播?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个奇怪的问题,逻辑调用上下文没有传播到应用程序中的线程.

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?

在我的应用程序中,我无法在实例化时设置逻辑调用上下文变量(这是在启动时通过IoC(它是WebAPI堆栈)通过IoC完成的),而只能在每次调用时进行.

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.

推荐答案

CallContext是一个专门的集合对象,类似于用于方法调用的线程本地存储",并提供每个执行逻辑线程唯一的数据槽.不会在其他逻辑线程上的调用上下文之间共享这些插槽.可以在CallContext沿着执行代码路径上下移动时将对象添加到CallContext中,并通过该路径上的各种对象进行检查.

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.

还有

CallContext中的所有方法都是静态的,并且可以在当前Thread的调用上下文中进行操作.

All methods in CallContext are static and operate on the call context in the current Thread.

仅阅读文档,您就会认为这种情况将行不通.这对于SetDataGetData方法是正确的;如果在一个线程上使用SetData,则必须在同一线程上使用GetData才能取回数据.

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.

但是,有一个例外.您已经在使用的SetLogicalDataGetLogicalData.它们被设计为通过逻辑线程"流过上下文,即通过同一线程传递到子线程.

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");

上方

var dispatcher = new MessageDispatcher();

即在创建和启动线程之前(在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.

这篇关于为什么逻辑调用上下文不跨线程传播?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆