要理解 C# 中的 volatile 关键字,就要先知道编译器背后的一个基本优化原理。比如对于下面这段代码:

public class Example
{public int x;public void DoWork(){x = 5;var y = x + 10;Debug.WriteLine("x = " +x + ", y = " +y);}
}

在 Release 模式下,编译器读取 x = 5 后紧接着读取 y = x + 10,在单线程思维模式下,编译器会认为 y 的值始终都是 15。所以编译器会把 y = x + 10 优化为 y = 15,避免每次读取 y 都执行一次 x + 5。但 x 字段的值可能在运行时被其它的线程修改,我们拿到的 y 值并不是通过最新修改的 x 计算得来的,y 的值永远都是 15

也就是说,编译器在 Release 模式下会对字段的访问进行优化,它假定字段都是由单个线程访问的,把与该字段相关的表达式运算结果编译成常量缓存起来,避免每次访问都重复运算。但这样就可能导致其它线程修改了字段值而当前线程却读取不到最新的字段值。为了防止编译器这么做,你就要让编译器用多线程思维去解读代码。告诉编译器字段的值可能会被其它线程修改,这种情况不要使用优化策略。而要做到这一点,就需要使用 volatile 关键字。

给类的字段添加 volatile 关键字,目的是告诉编译器该字段的值可能会被多个独立的线程改变,不要对该字段的访问进行优化。

使用 volatile 可以确保字段的值是可用的最新值,而且该值不会像非 volatile 字段值那样受到缓存的影响。好的做法是将每个可能被多个线程使用的字段标记为 volatile,以防止非预期的优化行为。

为了加深理解,我们来看一个实际的例子:

public class Worker
{private bool _shouldStop;public void DoWork(){bool work = false;// 注意:这里会被编译器优化为 while(true)while (!_shouldStop){work = !work; // do sth.}Console.WriteLine("工作线程:正在终止...");}public void RequestStop(){_shouldStop = true;}
}public class Program
{public static void Main(){var worker = new Worker();Console.WriteLine("主线程:启动工作线程...");var workerTask = Task.Run(worker.DoWork);// 等待 500 毫秒以确保工作线程已在执行Thread.Sleep(500);Console.WriteLine("主线程:请求终止工作线程...");worker.RequestStop();// 待待工作线程执行结束workerTask.Wait();//workerThread.Join();Console.WriteLine("主线程:工作线程已终止");}
}

在这个例子中,while (!_shouldStop) 会被编译器优化为 while(true)。我们可以看一下实际的运行效果来验证这一点。切换 Release 模式,按 Ctrl + F5 运行程序,运行效果始终如下:

程序运行后,虽然主线程在 500 毫秒后执行 RequestStop() 方法修改了 _shouldStop 的值,但工作线程始终都获取不到 _shouldStop 最新的值,也就永远都不会终止 while 循环。

我们修改一下程序,对 _shouldStop 字段加上 volatile 关键字:

public class Worker
{private volatile bool _shouldStop;public void DoWork(){bool work = false;// 获取的是最新的 _shouldStop 值while (!_shouldStop){work = !work; // do sth.}Console.WriteLine("工作线程:正在终止...");}// ...(略)
}

此时在主线程调用 RequestStop() 方法后,工作线程便立即终止了,运行效果如下图所示:

这说明加了 volatile 关键字后,程序可以实时读取到字段的最新值。

注意,一定要切换为 Release 模式运行才能看到 volatile 发挥的作用,Debug 模式下即使添加了 volatile 关键字,编译器也是不会执行优化的。

当然,并不是所有的类型都可以使用 volatile 关键字修饰的,常见的使用 volatile 的类型是这些简单类型:sbyte, byte, short, ushort, int, uint, char, float 和 bool,其它的请查看参考链接。

参考: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile

-

精致码农

带你洞悉编程与架构

↑长按图片识别二维码关注,不要错过网海相遇的缘分

[C#.NET 拾遗补漏]10:理解 volatile 关键字相关推荐

  1. 对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相

    一:背景 1. 讲故事 昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为:[C#.NET 拾遗补漏]10:理解 volatile 关键字 (https://www.cnblogs.com/will ...

  2. 【Java线程】深入理解Volatile关键字和使用

    目录 背景 volatile原理 volatile特性 可见性 有序性 原子性 使用场景 背景 理解volatile底层原理之前,首先介绍关于缓存一致性协议的知识. 背景:计算机在执行程序时,每条指令 ...

  3. 深入理解volatile关键字---缓存一致性原理

    volatile关键字与缓存一致性 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java ...

  4. 理解volatile关键字

    一. volatile的作用 1. 可见性 cpu 在变量赋值之后加上写屏障,使得对 volatile变量 以及之前变量的写都写入到主内存中 cpu 在变量读取之前加上读屏障,使得对 volatile ...

  5. 如何理解 JAVA 中的 volatile 关键字

    如何理解 JAVA 中的 volatile 关键字 最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂. ...

  6. volite java_如何理解 JAVA volatile 关键字

    最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂.所以学习过程中顺手翻译下来,一方面巩固知识,一方面希 ...

  7. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  8. volatile关键字——保证并发编程中的可见性、有序性

    文章目录 一.缓存一致性问题 二.并发编程中的三个概念 三.Java线程内存模型 1.原子性 2.可见性 3.有序性 四.深入剖析volatile关键字 1.volatile关键字的两层语义 2.vo ...

  9. java中volatile关键字

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

最新文章

  1. 区块链技术的本质是分布式数据库
  2. ※编程随笔※=☆编程基础☆=※№ SVN工具自动属性 $Author$ $Date$ $Revision$ $URL$ $Header$替换...
  3. 清北学堂培训2019.4.7
  4. Java动态代理与Cglib代理
  5. BUUCTF Dig the way
  6. Python Django 装饰器模式之二阶装饰器
  7. 我的一点企业做云经验
  8. 企业网络之间资源互访
  9. 递归算法,JavaScript实现
  10. EF with (LocalDb)V11.0
  11. 在Oracle中查询表的大小、表的占用情况和表空间的大小
  12. python画心形代码大全_七夕 - 程序员表白代码
  13. 土木想往土木软件开发方向发展,应该如何准备
  14. 悉尼大学计算机科学专业,悉尼大学计算机科学专业怎么样
  15. python平均值和加权平均值
  16. 计算机机房动环系属于什么分项,动环监控系统
  17. WIN10如何进入BIOS界面
  18. SNARK Design
  19. vue中使用el-table调整行间距
  20. 安全多方计算与证券业数据生态

热门文章

  1. 安装SQLserver2008
  2. 写给深圳首期Python自动化开发周未班的信
  3. 003Java语言环境搭建
  4. dpdk对虚拟化的支持调研
  5. C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质...
  6. 【6】JAVA---地址App小软件(QueryPanel.class)(表现层)
  7. extjs 前端js代码调用后台函数方法
  8. IHttpModule 与IHttpHandler的区别
  9. 博客暂停通知-------10.1~11.24
  10. q-dir 打不开文件_Q-Dir –多窗格文件管理器