博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅议“全局变量”、“多线程”和“编译器陷阱”
阅读量:6575 次
发布时间:2019-06-24

本文共 1149 字,大约阅读时间需要 3 分钟。

今天偶然看到一段代码,也看到了作者对此的说明,觉得很有意思:
public event EventHandler Started;
protected virtual void OnStarted(EventArgs e)
{
    EventHandler handler = Started;
    if (handler !=
null )
    {
        handler(
null , e);
    }
}

为什么要申明一个全局的事件变量 Started?一开始我也觉得很多余,后来听作者说这段代码可以用到多线程中,有可能正在判断事件变量Started的时候,它有可能被另外的一个线程给改变了,这里引入一个局部变量 handler,可以保留Started之前的对象引用,确保后面的事件能够得到正确的处理。

那么我们是否可以按照这个风格写下面类似的代码呢?

public object MyObject;

public  void OnFunction()
{
    object obj= MyObject;
    if (obj!= null)
    {
        //在这里对obj进行其它处理

    }

}

上面这段代码在一般情况下没有问题,在多线程下面也工作良好,但如果你启用了编译器优化,很不幸,这段代码被优化成了下面的样子:

public object MyObject;

public  void OnFunction()
{
    if (MyObject!= null)
    {
        //在这里对MyObject进行其它处理

    }

}

也就是说,MyObject 对象引用的代码被inline(内联)了,取消了局部变量object obj的定义,减少了对象数量和创建过程,有助于提高效率,如果这段代码被用于多线程中,噩梦很可能就来了,你不知道是谁修改了MyObject的值,这就是“编译器陷阱”!

类似的代码,为什么上面EventHandler Started 在多线程下工作的很好,而object MyObject 却不可以?原来,这其中有玄机,在.NET平台中,它采用了不同的优化策略,参加原博文中的说法:

如果我说,这样的代码明显是会被编译器优化掉的,因此这样写完全没有意义,怎样呢?毕竟EventHandler作为一个委托,并没有用volatile关键字声明(事实上事件不能声明为volatile,但可以在这里用Thread.VolatileRead(ref object)方法),使用时也没有用Interlocked来访问。我其实真没有想到那么远,不过CLR Via C#上给出了解释(记不得是哪一章了):JIT的编译器在这里会识别出这个写法并且确保不会把handler变量优化掉。真是万幸,但估计又成为了一个被学院派的诟病的特性。

原文地址:

转载地址:http://udgjo.baihongyu.com/

你可能感兴趣的文章
corosync配置与详解
查看>>
openssl校验SSL证书public key是否配对
查看>>
Jolt大奖获奖图书
查看>>
drools 将添加switch支持
查看>>
android中webview空间通过Img 标签显示sd卡中 的图片
查看>>
android socket编程实例
查看>>
使用SimpleDateFormat出现时差
查看>>
关于linux低端内存
查看>>
url 的正则表达式:path-to-regexp
查看>>
ubuntu 16.04 安装PhpMyAdmin
查看>>
安卓开启多个服务
查看>>
设置分录行按钮监听事件
查看>>
C Primer Plus 第5章 运算符、表达式和语句 5.2基本运算符
查看>>
蓝牙手柄按键码
查看>>
redis启动失败
查看>>
java并发库之Executors常用的创建ExecutorService的几个方法说明
查看>>
Spring框架错误之org.springframework.beans.factory.BeanCreationException
查看>>
23种设计模式(1):单例模式
查看>>
socket 编程入门教程(五)UDP原理:4、“有连接”的UDP
查看>>
linux sort 命令详解
查看>>