c# volatile 关键字的拾遗补漏

作者:精致码农 • 王亮 时间:2022-10-20 04:39:09 

要理解 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 运行程序,运行效果始终如下:

c# volatile 关键字的拾遗补漏

程序运行后,虽然主线程在 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() 方法后,工作线程便立即终止了,运行效果如下图所示:

c# volatile 关键字的拾遗补漏

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

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

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

作者:精致码农

出处:http://cnblogs.com/willick

联系:liam.wang@live.com

来源:https://www.cnblogs.com/willick/p/13889006.html

标签:c#,volatile,关键字
0
投稿

猜你喜欢

  • winform C#获得Mac地址,IP地址,子网掩码,默认网关的实例

    2021-06-28 20:45:50
  • IDEA2022中部署Tomcat Web项目的流程分析

    2023-02-26 17:19:09
  • TransmittableThreadLocal解决线程间上下文传递烦恼

    2023-11-09 17:09:35
  • Java String源码分析并介绍Sting 为什么不可变

    2021-09-23 06:10:42
  • Maven生命周期和及插件原理用法详解

    2021-09-17 09:47:19
  • Java并发编程之同步容器与并发容器详解

    2023-10-17 04:06:23
  • WPF中NameScope的查找规则详解

    2023-01-18 14:22:02
  • C#使用foreach语句遍历堆栈(Stack)的方法

    2021-11-03 08:03:38
  • 详解C#开发Android应用程序的流程

    2021-08-11 21:32:59
  • List集合对象中按照不同属性大小排序的实例

    2023-06-07 14:27:41
  • Springboot打包为Docker镜像并部署的实现

    2023-06-05 23:11:52
  • java编程基础之模仿用户登录代码分享

    2022-06-09 17:38:22
  • C++临时性对象的生命周期详细解析

    2023-01-22 04:26:01
  • 如何用IDEA调试BUG的几种方法

    2022-08-04 17:12:46
  • Java 按行读取文件按行写入文件并以空格分割字符串的方法

    2023-08-31 12:32:21
  • Android实现无标题栏全屏的方法

    2023-06-25 11:14:27
  • Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解

    2023-05-31 15:35:27
  • Android分屏多窗口的实践代码

    2021-06-21 21:34:58
  • Android RadioButton单选框的使用方法

    2021-10-02 14:37:20
  • OpenCV实现直线拟合

    2023-06-22 15:22:37
  • asp之家 软件编程 m.aspxhome.com