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

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

服务器之家 - 编程语言 - C# - c# volatile 关键字的拾遗补漏

c# volatile 关键字的拾遗补漏

2022-10-12 13:40精致码农 • 王亮 C#

这篇文章主要介绍了c# volatile 关键字的相关资料,帮助大家更好的理解和学习c#的相关知识,感兴趣的朋友可以了解下

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

?
1
2
3
4
5
6
7
8
9
10
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,以防止非预期的优化行为。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 关键字:

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

以上就是c# volatile 关键字的拾遗补漏的详细内容,更多关于c# volatile 关键字的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/willick/p/13889006.html

延伸 · 阅读

精彩推荐
  • C#C# 中的EventHandler实例详解

    C# 中的EventHandler实例详解

    本文通过案例实例介绍了c#中的eventhandler,需要的的朋友参考下吧...

    1sa2sa5042021-12-31
  • C#C#获取硬盘序列号的问题小结

    C#获取硬盘序列号的问题小结

    本文给大家分享C#获取硬盘序列号的问题及解决方法,非常不错,需要的朋友参考下...

    青春日记8952021-12-16
  • C#C#遍历得到checkboxlist选中值和设置选中项的代码

    C#遍历得到checkboxlist选中值和设置选中项的代码

    这篇文章主要介绍了C#遍历得到checkboxlist选中值和设置选中项的代码,代码简单易懂,具有参考借鉴价值,需要的朋友可以参考下...

    路小乙11942021-12-03
  • C#C#用timer实现背单词小程序

    C#用timer实现背单词小程序

    这篇文章主要为大家详细介绍了C#用timer实现背单词小程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    、你我山前没相见7462022-07-31
  • C#Unity3D自定义创建圆锥体

    Unity3D自定义创建圆锥体

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

    hellemic11562022-03-10
  • C#C# 基础入门--常量

    C# 基础入门--常量

    本文主要介绍了C#中常量的相关知识,具有很好的参考价值。下面跟着小编一起来看下吧...

    辰星crystal6772021-12-28
  • C#C#通过NPOI操作Excel的实例代码

    C#通过NPOI操作Excel的实例代码

    C#操作Excel的方法有很多种,本文介绍了C#通过NPOI操作Excel,具有一定的参考价值,有兴趣的可以了解一下。...

    独钓寒江雪丶6362021-12-18
  • C#实例分享C#中Explicit和Implicit用法

    实例分享C#中Explicit和Implicit用法

    本篇文章主要给读者们分享了C#中Explicit和Implicit的用法,对此有需求和兴趣的朋友们一起学习下吧。...

    LamondLu的小窝10552022-02-17