你知道吗?多个类多线程环境下静态构造函数的执行顺序
调用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%吧。
- Web Spider实战1——简单的爬虫实战(爬取"豆瓣读书评分9分以上榜单")
- 如何用R语言从网上读取多样格式数据
- C/C++——生成随机数
- PHP基础——PHP数组
- 使用shell抽取html数据之二(r2笔记75天)
- Python爬取链家网数据:新房楼盘价格分析
- 【编程基础】Java里面如何对字符串排序?
- 计算广告——广告定向实践
- 通过shell抓取html数据(r2笔记74天)
- 通过shell脚本分析足彩(r2笔记74天)
- 通过shell脚本得到数据字典的信息 (r2笔记72天)
- 机器学习算法实践——K-Means算法与图像分割
- 利用 Python、SciKit 和文本分类来构建客户行为描述模型
- 使用Python爬取社交网络数据分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) C. Remove Adjacent
- MySQL 存储过程
- MySQL 约束
- MySQL 中的流程控制语句
- MySQL 权限操作
- MySQL 事务
- Java 随机生成四则运算式并生成答案
- MySQL 常用函数汇总
- Leetcode 698. 划分为k个相等的子集
- java数据结构与算法-快速排序
- 线上环境 Linux 系统调用追踪
- Kubernetes 1.19.0——其他控制器
- leetcode树之二叉树的所有路径
- Nginx 防盗链
- MySQL见闻录 - 入门之旅