zoukankan      html  css  js  c++  java
  • 你知道吗?多个类多线程环境下静态构造函数的执行顺序

    调用A a=new A()
    请问输出是什么?为什么?

     
    class A
    {
        static A()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            XTrace.WriteLine("A1");
            Thread.Sleep(3000);
    
            //B b = new B();
            XTrace.WriteLine("AA");
            //ThreadPool.QueueUserWorkItem(delegate { XTrace.WriteLine("BB"); B b = new B(); });
            Thread thread = new Thread(new ParameterizedThreadStart(delegate { XTrace.WriteLine("BB"); B b = new B(); }));
            thread.Start();
    
            Thread.Sleep(3000);
            sw.Stop();
            XTrace.WriteLine("A2 " + sw.Elapsed);
        }
    
        public A() { XTrace.WriteLine("new A"); }
    }
    class B
    {
        static B()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            XTrace.WriteLine("B1");
            Thread.Sleep(3000);
    
            A a = new A();
    
            Thread.Sleep(3000);
            sw.Stop();
            XTrace.WriteLine("B2 " + sw.Elapsed);
        }
    
        public B() { XTrace.WriteLine("new B"); }
    }
    
    
    

    关于静态构造函数函数的基本常识就不多说,园子里随处可见!

    这个问题让群里的高手纠结了一整天,那个线程为什么不动?(线程等到A静态构造函数执行完毕后才执行)

    傍晚时分,有人忍不住发信问微软:

    Z_(164734xxx) 19:19:25
    A static constructor is never called more than once, and it must be finished before any other thread can create an instance of the class or use a static member of the class. Therefore, the thread you try starting cannot start before A's static constructor ends.

    
    
    

    网上很多资料说到静态构造函数,但是很少提到与线程相关的,这个例子实际上是想测试一下静态构造函数的多线程冲突。

    其实,这个问题源自于XCode v7.3中一个隐秘的BUG。

    实体类A的静态构造函数中可能会开一个线程去执行方法B,然后静态构造函数接着执行后续方法C,问题就在于B和C都会争夺同一个锁,如果B拿到这个锁,它会创建一个A的实例,但是因为A的静态构造函数正常执行C,C又等待B释放这个锁,从而形成了死锁,所有用到类型A的线程都会挂起。

    因为B和C的执行速度不一样,要是C先拿到资源,就不会出现死锁,所以这个问题解决起来特别的麻烦!

    XCode v7.3的这个BUG表明,那个线程应该是可以同步执行的,但是为什么测试项目里面线程就是不动呢?(先看看大家讨论,后面再公布答案)

    附上XCode中出错的部分

    /// <summary>
    /// 数据实体类基类。所有数据实体类都必须继承该类。
    /// </summary>
    [Serializable]
    public partial class Entity<TEntity> : EntityBase where TEntity : Entity<TEntity>, new()
    {
        #region 构造函数
        /// <summary>
        /// 静态构造
        /// </summary>
        static Entity()
        {
            // 1,可以初始化该实体类型的操作工厂
            // 2,CreateOperate将会实例化一个TEntity对象,从而引发TEntity的静态构造函数,
            // 避免实际应用中,直接调用Entity的静态方法时,没有引发TEntity的静态构造函数。
            TEntity entity = new TEntity();
            EntityFactory.CreateOperate(Meta.ThisType, entity);
        }
    

    TEntity就是实体类,它本身也有静态构造函数,并且它的静态构造函数里面会开一个线程去调用EntityFactory.CreateOperate(Type type),该方法会取得一个字典的锁,然后通过Activator.CreateInstance(type)创建类型type的实例,加入字典,也就是实体类本身的实例。

    EntityFactory.CreateOperate(Type type, IEntityOperate entity)跟上面的EntityFactory.CreateOperate(Type type)共同点再也它也要那这个字典的锁,不同的在于它只是把entity加入字典。

    结果就是:如果两个参数这个先执行,就没有问题,如果一个参数那个先执行,大家一起死!

    答案:

    上面微软的答复邮件说得很清楚,静态构造函数只会被调用一次,并且在它执行完成之前,任何其它线程都不能创建这个类的实例或使用这个类的静态成员!

    这里面包含几层一次:

    1,静态构造函数只会被调用一次,并且在所有对该类的访问之前。这一点我确信99.99%的人都知道。

    2,“其它线程”。也就是说,只是其它线程不能创建实例和调用静态成员而已,当前线程仍然是可以的。

    3,“创建实例或使用静态成员”。那么实例成员呢?当然不可能了,因为实例都无法创建,如何使用实例成员?

    4,也是最隐秘的地方。测试代码中,在A的静态构造函数里面使用了匿名函数,而编译器会把它编译成为A的一个静态方法,因此,它就成了A的静态成员了,所以……

    实际上,我们没注意到的地方是第四点,太粗心了!

    不过,可能清楚第二点的人不到10%吧。

  • 相关阅读:
    css圆角与阴影,[iecss3.htc文件需要下载]
    [纯css下拉菜单]兼容各种IE,但搜狗好像有点问题
    UITabBar的隐藏与显示
    UIScrollView属性总结
    NSTimer 用法小结
    自定义UINavigationController的push和pop切换界面动画
    UIScrollView实现缩放和居中
    UITableView内存警告销毁视图后重新load视图,cell未显示内容
    Select的使用
    FreeType的使用
  • 原文地址:https://www.cnblogs.com/nnhy/p/cctor.html
Copyright © 2011-2022 走看看