CallContext类

本文详细探讨了.NET框架中CallContext类的功能及其在不同线程间的传递特性,并通过实验验证了子线程和异步线程无法访问主线程CallContext中数据的现象。此外,还对比了ThreadStaticAttribute的作用及其实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

System.Runtime.Remoting.Messaging.CallContext类

MSDN:

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 Data。Damn! Is it so?

ContractedBlock.gifExpandedBlockStart.gifCode
    class Program
    {
        
static void Main(string[] args)
        {
            C1
<intstring> x = new C1<intstring>();
            x.Prop1 
= 999;
            x.Prop2 
= "bbac";
            
string s = x.Prop2 + x.Prop1.ToString();
            GetSetCallContextData();
        }

        
private static void GetSetCallContextData()
        {
            DateTime now 
= DateTime.Now;
            Console.WriteLine(
"Set outer thread time:{0}", now);
            CallContext.SetData(
"now", now);

            Thread.Sleep(
3000);

            AutoResetEvent are 
= new AutoResetEvent(false);
            Thread t 
= new Thread(new ParameterizedThreadStart((x) =>
            {
                AutoResetEvent outerAre 
= x as AutoResetEvent;

                
object outerTime = CallContext.GetData("now");
                Console.WriteLine(
"Get outer thread time for inner thread:{0}", outerTime);

                DateTime innerNow 
= DateTime.Now;
                Console.WriteLine(
"Set inner thread time:{0}", innerNow);
                CallContext.SetData(
"now", innerNow);

                outerAre.Set();
            }));
            t.Start(are);


            are.WaitOne();

            DateTime theTime 
= (DateTime)CallContext.GetData("now");
            Console.WriteLine(
"Get time from outer thread:{0}", theTime);


            Func
<bool> m = () =>
            {
                
object theTime2 = CallContext.GetData("now");
                Console.WriteLine(
"Get time from outer thread for async thread:{0}", theTime2);
                
return true;
            };

            var result 
= m.BeginInvoke((x) => { }, 1);
            m.EndInvoke(result);

            Console.Read();
        }

 

经过测试发现,没错,我被人忽悠了!

可以看到,子线程和异步线程都无法访问到主线程在CallContext中保存的数据。

另外一点,当使用ASP.NET的时候,虽然线城池里的线程是复用的,但是CallContext并不在一个线程的多次使用中共享。因为CallContext是针对逻辑线程的TLS,线程池中被复用的线程是操作系统中的内核对象而不是托管对象。就像数据库连接池中保存的是非托管资源而不是托管资源。因此,先后执行的两个托管线程可能在底层复用了一个物理线程(内核对象),但并不能共享同一组CallContext数据槽。就像先后new的两个SqlConnection对象可能在底层使用了同一个物理连接,但是托管对象的属性已经被重置。

与此对照的是ThreadStaticAttribute,标记上这个特性的静态字段是往物理线程的TLS中保存数据(根据MSDN的描述猜的。具体没试过),因此如果两个托管线程对象内部使用的是同一个物理线程,则这个字段会复用(在两个线程通过这一字段访问同一个数据槽)。

哎,算了。虽让哥们闲得没事干呢?把ThreadStatic的测试代码也贴出来:

 

ContractedBlock.gifExpandedBlockStart.gifCode
   class Program
    {
        [ThreadStatic]
        
static int a;

        
static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(
11);

            ThreadPool.QueueUserWorkItem((x) 
=>
            {
                a 
= 1997;

            });

            ThreadPool.QueueUserWorkItem((x) 
=>
            {
                Console.WriteLine(
"a={0}", a);

            });

            Thread.Sleep(
2000);
            Console.WriteLine(a);

            Console.Read();

        }
}

 

可以看到我的猜测是正确的。ThreadStatic的效果是由JIT实现的。

园子里有个牛人博客的标题是First we try, then we trust.

到我这是:Fist I guess, then I wrong, then I try, then I trust. 

 

转载于:https://www.cnblogs.com/zhy2002/archive/2008/11/18/1335821.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值