时间:2021-05-19
前言
编写程序的时候,人们的直观感觉通常认为,程序的执行顺序是按照语句的顺序进行的。然而,许多编程语言的规范是允许实际执行顺序与语句编写顺序不符的。实际上,编译器为了完成某种优化,常常会对一些操作进行适当的顺序调整,导致一些预料之外的现象。
实验现象
首先,通过一个例子来展示这个现象。在一个C# .NET Core 3.1命令行程序中,定义两个全局变量a和b,在线程1中,依次对b和a进行递增。这样,在任何时刻b应当等于a或a+1。
static int a = 0; static int b = 0; static void Thread1() { while (true) { ++b; ++a; } }在线程2中,先读取a的值,然后执行一些其他操作,再读取b的值。如果语句一定是按顺序执行的,那么读取到的b的值应当比读取到的a的值更新,从而b必然大于或等于a(除非b发生了溢出)。编写程序,当b < a时输出它们的值。
static int c = 0; static void Thread2() { while (true) { c += b; var localA = a; c += b; var localB = b; if (localA > localB) { Console.WriteLine($"a={localA} b={localB}"); } } }再编写主程序,启动上述的两个线程。
static void Main(string[] args) { Task.Run(Thread1); Task.Run(Thread2); Console.ReadKey(); }使用Debug配置,编译并运行该程序,命令行是没有输出的,符合我们的预期。但是使用Release配置的话,就会出现大量输出,其中a的值比b大1到5不等。
查看反汇编可以看到,在第1个c += b语句处,程序将b的值放到了寄存器中,而后面的语句均使用了该寄存器内存放的值。所以,编译器实际上将对b的读取操作合并并且前置了。以下为反汇编结果片段。
00007FFB628A394D mov rcx,7FFB6292FBD0h 00007FFB628A3957 mov edx,1 00007FFB628A395C call 00007FFBC2387B10 00007FFB628A3961 mov esi,dword ptr [7FFB6292FC08h] 00007FFB628A3967 mov ecx,esi 00007FFB628A3969 add ecx,dword ptr [7FFB6292FC0Ch] 00007FFB628A396F mov dword ptr [7FFB6292FC0Ch],ecx var localA = a;00007FFB628A3975 mov edi,dword ptr [7FFB6292FC04h] c += b;00007FFB628A397B add ecx,esi c += b;00007FFB628A397D mov dword ptr [7FFB6292FC0Ch],ecx if (localA > localB)00007FFB628A3983 cmp edi,esi 00007FFB628A3985 jle 00007FFB628A394D理论分析
在C#语言标准的Basic concepts一章Execution order一节(参见:Basic concepts – C# language specification)中,提到了C#的执行顺序规范。C#程序的副作用在以下关键点处的顺序是被保留的:
C#程序的执行顺序在满足以下条件的情况下,可以由执行环境任意调整的:
而上述的副作用包括:
由此可以推出,C#程序中对非volatile变量的读取顺序可能会被调整。在只有一个线程对该变量进行操作时,这个顺序的调整是保证不会影响结果的;但如果同时有其他的线程正在对变量进行修改,则读取的顺序是无法确定的。
因此,如果有多个线程同时访问的,对值的实时性有要求的变量,应当设置为volatile变量。将上述实验中的静态变量a和b改为volatile变量后,即使是Release配置下,也不会出现命令行的输出,即两个变量的读取顺序符合原始的语句顺序。
结论
在C#程序中,读取非volatile变量的顺序可能被执行环境任意调整。如果某个变量在被读取的时候会被其他线程写入,为了该读取结果的实时性,应当将该变量设置为volatile变量。
总结
到此这篇关于关于C#执行顺序带来的一些潜在问题就介绍到这了,更多相关C#执行顺序潜在问题内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
1.顺序结构:-->所谓顺序结构顾名思义,就是按顺序执行,在C#中所有的代码都是由上至下的顺序执行。2.分支结构:-->三元表达式?:,if...else...
引言对于一些刚接触C#不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友
本文实例讲述了C#递归读取XML菜单数据的方法。分享给大家供大家参考。具体分析如下:最近在研究一些关于C#的一些技术,纵观之前的开发项目的经验,做系统时显示系统
下面通过一段代码给大家解析C#语句的顺序不同所执行的结果不一样。usingSystem;usingSystem.Collections.Generic;usin
本文实例讲述了C#执行DOS命令的方法。分享给大家供大家参考。具体实现方法如下:在c#程序中,有时会用到调用cmd命令完成一些功能,本文介绍的如下方法,可实现c