前一段时间,项目组在开发过程中发现,在同一个页面使用两个HSCombobox同时访问一个WebService时会报错。根据项目组的反馈,我在系统框架的示例项目中做了重现,跟踪源代码时发现,报错的位置在HSEntityWebValidator的_cache.Add(typeName, validator);,提示信息为未将对象引用设置到对象实例。刚开始看到这个信息很迷茫,觉得无从下手,后来反复看了一下系统框架中的代码,发现系统框架中使用了全局静态变量Dictionary。
查阅相关资料后,了解到Dictionary类型为弱引用类型,当没有对象调用的时候,弱引用类型会被.Net的垃圾回收机制自动回收以避免内存溢出,回收之后再去引用这个对象就找不到这个对象了,所以会报这个错误。
找到了问题的原因,查找解决方案。在C# 4.0之前,解决线程安全问题的方式是通过Lock的方式来处理的。而我们的系统框架所使用的C#版本为C# 5.0,解决问题的办法也应该有所创新。在C# 4.0以后,.Net提供了一个ConcurrentDictionary,它是线程安全的,于是就用它替换了Dictionary类型,测试发现问题解决。
但其能否完全替代Dictionary+Lock的方式保证线程安全,这个还有待于进一步验证。
查阅相关资料后,了解到Dictionary类型为弱引用类型,当没有对象调用的时候,弱引用类型会被.Net的垃圾回收机制自动回收以避免内存溢出,回收之后再去引用这个对象就找不到这个对象了,所以会报这个错误。
但这并不是引发错误的根本原因,根据项目组的描述,应该是多个线程并发访问Dictionary导致的报错,作为全局的静态变量在多线程中是共享的,而Dictionary本身也是非线程安全的。但根据项目组的描述我在一个页面上使用两个HSCombobox访问同一个WebService没有出现异常,于是我在测试项目中模拟了并发的场景来验证我的猜测:
Parallel.For(1, 100, k =>
{
HSDTOWebModels.GetWebModel(typeof(UserDTO));
});
测试结果抛出异常……据推测可能是并发线程数量较少,.Net自己优化了线程导致的不报错。
找到了问题的原因,查找解决方案。在C# 4.0之前,解决线程安全问题的方式是通过Lock的方式来处理的。而我们的系统框架所使用的C#版本为C# 5.0,解决问题的办法也应该有所创新。在C# 4.0以后,.Net提供了一个ConcurrentDictionary,它是线程安全的,于是就用它替换了Dictionary类型,测试发现问题解决。
但其能否完全替代Dictionary+Lock的方式保证线程安全,这个还有待于进一步验证。