原本我一直以为nop对程序语义应该没什么影响的,呜,我错了。
之前一直觉得很奇怪,为什么不开/o(/optimize,优化)开关来编译C#程序的话,生成的IL代码里会有很多nop。后来才知道那些nop是在一些语句或者表达式片段的边界放的,目的是为了提供更好的调试体验:有些代码被编译之后可能会变得比较难下断点,所以插入nop来占位,使得原本难下断点的地方能被设置断点。
但是插入nop的代价不只是使程序的运行速度降低;更糟糕的是,它可能会改变程序的语义使本来应该正常的程序变得无法正确运行。
Eric Lippert在[url=http://blogs.msdn.com/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx]去年的一篇blog[/url]里提到了这点。他举的例子是C#里的lock语句:
会被编译成类似这样:
这本来没什么问题,但是当C#编译器在Enter()与try之间插入nop时问题就来了:如果另外一个线程在当前线程刚获取锁在nop的时候引发了线程终止异常,那么finally子句就永远不会被执行,于是就是……死锁。
据说这个问题在C# 3.0里也没解决。不过还好遇到这种问题的可能性很小。
Hmm。果然遇上多线程之后很多“常识”都得重新想想。
之前一直觉得很奇怪,为什么不开/o(/optimize,优化)开关来编译C#程序的话,生成的IL代码里会有很多nop。后来才知道那些nop是在一些语句或者表达式片段的边界放的,目的是为了提供更好的调试体验:有些代码被编译之后可能会变得比较难下断点,所以插入nop来占位,使得原本难下断点的地方能被设置断点。
但是插入nop的代价不只是使程序的运行速度降低;更糟糕的是,它可能会改变程序的语义使本来应该正常的程序变得无法正确运行。
Eric Lippert在[url=http://blogs.msdn.com/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx]去年的一篇blog[/url]里提到了这点。他举的例子是C#里的lock语句:
lock(expression) statement会被编译成类似这样:
temp = expression;
System.Threading.Monitor.Enter(temp);
try { statement } finally { System.Threading.Monitor.Exit(temp); }这本来没什么问题,但是当C#编译器在Enter()与try之间插入nop时问题就来了:如果另外一个线程在当前线程刚获取锁在nop的时候引发了线程终止异常,那么finally子句就永远不会被执行,于是就是……死锁。
据说这个问题在C# 3.0里也没解决。不过还好遇到这种问题的可能性很小。
Hmm。果然遇上多线程之后很多“常识”都得重新想想。
3414

被折叠的 条评论
为什么被折叠?



