在.NET 4.0之后,.NET Framework中提供了一种安全的延迟加载类型Lazy
Lazy能够在多线程环境下,保证GetValue函数只执行一次,从而实现单例模式

在过去,实现单例模式我们通常使用二次判断锁,或者利用类的静态初始化函数
利用Lazy类型,能够简化这一过程,并且性能上更好

Lazy创建的时候可以指定线程安装模式,目前有两种模式,PublicationOnly,ExcutionAndPublication

PublicationOnly模式

                boxed = CreateValue(); //
if (boxed == null ||
Interlocked.CompareExchange(ref m_boxed, boxed, null) != null) //
{
boxed = (Boxed)m_boxed; //
}
else
{
m_valueFactory = ALREADY_INVOKED_SENTINEL; //
}

1.运行初始化函数,装箱到一个内部Box类型中,解决null值判断的问题,如果已经创建的情况,会返回null,该过程是线程不安全的

2.判断m_boxed是否为空,m_boxed是value保存的字段,如果等于空则设置为boxed,该方法能保证原子性,该过程是线程安全的

3.如果CreateValue返回空,表示其他线程已经创建有实例,则设置为已经创建好的实例

4.将初始化方法标记为已经初始化,一般发生在并发运行情况下,多次运行CreateValue

PublicationOnly模式下使用基于Interlocked.CompareExchange实现的乐观锁,该类包含了原子性方法 CAS(Compare and swap)

CAS是利用CPU提供的原子性指令来实现,不同运行时版本可能有不一样实现
Interlocked具体的实现在Native方法中,有兴趣的朋友可以通过coreclr/jvm代码查看具体实现

这种模式下,单例函数可能多次运行,但是最终能保证获取到的实例只有一个

ExcutionAndPublication模式下使用的是Volatile+MonitorMonitor就是lock语句的实现,Monitor实现在Native代码中,是重量级的锁

Monitor支持队列和线程睡眠,能够保证一整个方法块处于单线程执行状态

                object threadSafeObj = Volatile.Read(ref m_threadSafeObj); //强制从主内存空间同步变量到线程内存空间副本
bool lockTaken = false;
try
{
if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL) //此时会有多个线程获取到正确值,抢夺开始
Monitor.Enter(threadSafeObj, ref lockTaken); //尝试等待锁,进入成功设置lockTaken为true
else
Contract.Assert(m_boxed != null);
            //单线程代码块 Start
if (m_boxed == null) //没有设置值的情况
{
boxed = CreateValue(); //获取值
m_boxed = boxed; //设置到字段中
Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL); //强制将线程内存空间副本写入到主内存空间
}
else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so
{
boxed = m_boxed as Boxed;
if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder
{
LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder;
Contract.Assert(exHolder != null);
exHolder.m_edi.Throw();
}
}
            //单线程代码块End
}
finally
{
if (lockTaken) //进入成功需要释放,避免死锁
Monitor.Exit(threadSafeObj);
}

最新文章

  1. unrar.dll 使用实例
  2. ThoughtWorks.QRCode生成二维码
  3. 按列 sort 排序 Linux 如何查看当前占用CPU或内存最多的K个进程
  4. [Sqlite] --> Sqlite于Windows、Linux 和 Mac OS X 在安装过程
  5. Android Services (后台服务)
  6. Python类中的self到底是干啥的
  7. 21.C++- "++"操作符重载、隐式转换之explicit关键字、类的类型转换函数
  8. Eureka配置instanceId显示IP
  9. Python爬虫之正则表达式(2)
  10. [math]本博客已经支持书写数学公式
  11. Navicat 连接MySQL时出现1251错误的解决方案
  12. 用JavaScript实现点击左侧列表右侧显示列表内容的方法
  13. Linux的基础命令
  14. 解决:angularjs radio默认选中失效问题
  15. POJ 2970 The lazy programmer
  16. WP8 调用webservice 错误 The remote server returned an error: NotFound 解决
  17. Thunder7.2.13.3884 JayXon
  18. linux修改系统时间时区
  19. C#中的多线程 - 并行编程 z
  20. 添加路由时啥时候是dev啥时候是gw

热门文章

  1. 2018.09.01 poj2689 Prime Distance(埃式筛法)
  2. docker入门实战
  3. SpringMVC上传图片总结(2)--- 使用百度webuploader上传组件进行上传图片
  4. Tensorflow currently has no official prebuild for your CUDA, cuDNN combination.
  5. MFC 怎样获得某个窗口的句柄?
  6. PAT甲 1046. Shortest Distance (20) 2016-09-09 23:17 22人阅读 评论(0) 收藏
  7. node csv
  8. Centos 7 安装 FFmpeg
  9. AI_ 视频监控-人体移动捕捉监测
  10. C#: 线程间操作无效: 从不是创建控件“dataGridView”的线程访问它