服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 关于C#执行顺序带来的一些潜在问题

关于C#执行顺序带来的一些潜在问题

2022-10-07 16:01UWP爱好者 C#

这篇文章主要给大家介绍了关于C#执行顺序带来的一些潜在问题,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

编写程序的时候,人们的直观感觉通常认为,程序的执行顺序是按照语句的顺序进行的。然而,许多编程语言的规范是允许实际执行顺序与语句编写顺序不符的。实际上,编译器为了完成某种优化,常常会对一些操作进行适当的顺序调整,导致一些预料之外的现象。

实验现象

首先,通过一个例子来展示这个现象。在一个C# .NET Core 3.1命令行程序中,定义两个全局变量a和b,在线程1中,依次对b和a进行递增。这样,在任何时刻b应当等于a或a+1。

?
1
2
3
4
5
6
7
8
9
10
11
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时输出它们的值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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}");
    }
  }
}

再编写主程序,启动上述的两个线程。

?
1
2
3
4
5
6
7
static void Main(string[] args)
{
  Task.Run(Thread1);
  Task.Run(Thread2);
 
  Console.ReadKey();
}

使用Debug配置,编译并运行该程序,命令行是没有输出的,符合我们的预期。但是使用Release配置的话,就会出现大量输出,其中a的值比b大1到5不等。

查看反汇编可以看到,在第1个c += b语句处,程序将b的值放到了寄存器中,而后面的语句均使用了该寄存器内存放的值。所以,编译器实际上将对b的读取操作合并并且前置了。以下为反汇编结果片段。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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#程序的副作用在以下关键点处的顺序是被保留的:

  • 对volatile字段的读写
  • lock语句
  • 线程的创建和终结

C#程序的执行顺序在满足以下条件的情况下,可以由执行环境任意调整的:

  • 在同一线程内,数据的的依赖关系是被保留的。即,结果与语句按照顺序执行的情况一致。
  • 初始化顺序的规则是被保留的。
  • 相对于volatile字段的读写,副作用的顺序是被保留的。

而上述的副作用包括:

  • 读取或写入volatile字段
  • 写入非volatile变量
  • 写入外部资源
  • 抛出异常

由此可以推出,C#程序中对非volatile变量的读取顺序可能会被调整。在只有一个线程对该变量进行操作时,这个顺序的调整是保证不会影响结果的;但如果同时有其他的线程正在对变量进行修改,则读取的顺序是无法确定的。

因此,如果有多个线程同时访问的,对值的实时性有要求的变量,应当设置为volatile变量。将上述实验中的静态变量a和b改为volatile变量后,即使是Release配置下,也不会出现命令行的输出,即两个变量的读取顺序符合原始的语句顺序。

结论

在C#程序中,读取非volatile变量的顺序可能被执行环境任意调整。如果某个变量在被读取的时候会被其他线程写入,为了该读取结果的实时性,应当将该变量设置为volatile变量。

总结

到此这篇关于关于C#执行顺序带来的一些潜在问题就介绍到这了,更多相关C#执行顺序潜在问题内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.taoyouh.cn/archives/2056

延伸 · 阅读

精彩推荐
  • C#深入浅出23种设计模式

    深入浅出23种设计模式

    本文给大家详解23种设计模式,理解设计模式有助于在程序开发过程中灵活应用,需要的朋友可以参考下...

    编程技术网10762021-10-22
  • C#Unity3D自定义创建圆锥体

    Unity3D自定义创建圆锥体

    这篇文章主要为大家详细介绍了Unity3D自定义创建圆锥体,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    hellemic11562022-03-10
  • C#C#修改IIS站点framework版本号的方法

    C#修改IIS站点framework版本号的方法

    这篇文章主要介绍了C#修改IIS站点framework版本号的方法,涉及C#调用使用ASP.NET IIS注册工具Aspnet_regiis.exe进行IIS站点framework版本号修改的方法,具有一定参考借鉴...

    蓝图9172021-10-28
  • C#WinForm遍历窗体所有子控件的方法

    WinForm遍历窗体所有子控件的方法

    这篇文章主要介绍了WinForm遍历窗体所有子控件的方法,涉及C#递归遍历相关操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    HTL9312021-11-30
  • C#c#中的扩展方法学习笔记

    c#中的扩展方法学习笔记

    扩展方法能够向现有类型“添加”方法,而无需创建新的派生类型,重新编译或以其他方式修改原始类型。下面这篇文章主要给大家介绍了关于c#中扩展方...

    alone_alone4342022-03-05
  • C#C# 命名空间(Namespace)相关知识总结

    C# 命名空间(Namespace)相关知识总结

    这篇文章主要介绍了C# 命名空间(Namespace)的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以参考下...

    菜鸟教程11412022-09-16
  • C#C#程序中使用LINQ to XML来查询XML格式数据的实例

    C#程序中使用LINQ to XML来查询XML格式数据的实例

    这篇文章主要介绍了C#程序中使用LINQ to XML来查询XML格式数据的实例,LINQ to XML是.NET框架中集成的接口,可以将XML数据放到内存中进行处理,需要的朋友可以参考...

    DebugLZQ6662021-11-15
  • C#C#微信接口之推送模板消息功能示例

    C#微信接口之推送模板消息功能示例

    这篇文章主要介绍了C#微信接口之推送模板消息功能,结合实例形式分析了C#操作微信推送模板消息接口的具体操作步骤与相关使用技巧,需要的朋友可以参考...

    兰給3592022-01-12