千家信息网

C#中Volatile的使用方法有哪些

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容介绍了"C#中Volatile的使用方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
千家信息网最后更新 2026年11月03日C#中Volatile的使用方法有哪些

本篇内容介绍了"C#中Volatile的使用方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1.Overview

经常研究.NET源码库的小伙伴会经常看到一个关键字volatile,那它在开发当中的作用是什么呢?

我们一起来看看官方文档里是怎么描述的,如下:

"volatile 关键字指示一个字段可以由多个同时执行的线程修改。出于性能原因,编译器,运行时系统甚至硬件都可能重新排列对存储器位置的读取和写入。声明为 volatile 的字段将从某些类型的优化中排除。不确保从所有执行线程整体来看时所有易失性写入操作均按执行顺序排序。"

本文将围绕这部分进行解读。

声明语法如下:

class VolatileTest{   public volatile int sharedStorage;      public void Test(int i)  {       sharedStorage = i;  }}

2.Detail

我们先了解一下前置知识点。

(1)在CLR中将对sbyte、byte、short、ushort、int、uint、char、float 和 bool。以及引用类型保证读写时原子性的(long、double不是原子性读写)变量中的所有字节都是一次性写入或读取的。

(2)Framework Class Library(FCL) 保证所有静态方法都是线程安全的。这意味着假如两个线程同时调用一个静态方法,不会有数据被损坏。为什么?

public static string Print(String str){   string val = "";   val += str;   return val;}

因为静态方法内声明的变量,每个线程调用时都会新创建一份,而不会共用一个存储单元。比如这里的val每个线程都会创建自己的一份,因此不会有线程安全问题。注意:静态变量,由于是在类加载时占用一个存储区每个线程都是共用这个存储区的,所以如果在静态方法里使用了静态变量;这就会有线程安全问题。

(3)内存、CPU缓存(注:下列为简述内容,实际上不仅如此)

CPU缓存,CPU集成的缓存。

内存,内存条硬件提供的存储空间。

我们继续回到主要内容上,用下面的若干代码示例来表达volatile的作用。

public class Program{       public static int bookNum = 0;       public static void Main(string[] args)      {           Console.WriteLine("juster书的数量:" + bookNum);           Thread juster = new Thread(() =>          {               Console.WriteLine("juster没带书,等待家长送书到学校...");               while (bookNum == 0) {}               Console.WriteLine("juster拿到书,开始上课听讲。");          });           juster.Name = nameof(juster);           juster.Start();           Thread parent = new Thread(() =>          {               Console.WriteLine("parent在屋里找书中...");               Thread.Sleep(2000);               Console.WriteLine("parent找到了书之后,送往学校...");               SendBook();          });           parent.Name = nameof(parent);           parent.Start();      }       public static void SendBook()      {           bookNum = 1;      }}

代码执行输出如下:

这时候诡异的来了,按照正常的代码执行逻辑不难看出当parent线程执行Sendbook()的时候juster应该就能拿到书上课了。但是这里juster却一直没有拿到是为什么呢?

心细的小伙伴应该观察到了这里的运行模式是Release,众所周知Release是.Net的发布版本执行效率会比Debug版本要高。

为什么Release版本效率高呢?怎么得来的?下面这段代码来解释:

上面这张反编译的图不难看出,10*10-100这段代码直接编译成0了。这种现象是因为Release编译的时候编译器会对代码进行'优化'。这段是最直观能看到的'优化'效果,其实C#编译器将你的代码转换成中间语言(IL)。然后,JIT将IL转换成本机CPU指令。此外,C#编译器、JIT编译器,甚至CPU本身都可能优化你的代码。

但是实际上在上述代码中count的值始终为0;所以循环永远不会执行,没有必要编译循环内的代码在编译后会被'优化'。说了这么多,只是为了给大伙证明Release编译这一层会存在'优化';接下来继续回到volatile上。

说到这里,如何解决各种'优化'带来的问题呢?这时候只需要在booknum前面加上volatile关键字修饰即可。

public class Program{       public static volatile int bookNum = 0;       public static void Main(string[] args)      {           Console.WriteLine("juster书的数量:" + bookNum);           Thread juster = new Thread(() =>          {               Console.WriteLine("juster没带书,等待家长送书到学校...");               while (bookNum == 0) { }               Console.WriteLine("juster拿到书,开始上课听讲。");          });           juster.Name = nameof(juster);           juster.Start();           Thread parent = new Thread(() =>          {               Console.WriteLine("parent在屋里找书中...");               Thread.Sleep(2000);               Console.WriteLine("parent找到了书之后,送往学校...");               SendBook();          });           parent.Name = nameof(parent);           parent.Start();      }       public static void SendBook()      {           bookNum = 1;      }}

在被各种优化之后,booknum因为是值类型在每个线程访问时会发生复制且又是在静态方法中被修改。所以每个线程都会复制booknum的值到当前线程上下文中缓存起来。这样就导致了parent线程修改了booknum的值juster线程看不到的情况。这个时候就需要用volatile关键字告诉编译器不需要这样的优化,表示用volatile定义的变量会被改变,每次都必须从内存中读取,而不能把他放在CPU cache或寄存器中重复使用。最后booknum会在运行的过程中修改值且其他线程能'共享访问'达到最终的效果。

3.Conclusion

Part1

volatile 关键字可应用于以下类型的字段:

  • 引用类型。

  • 指针类型(在不安全的上下文中)。请注意,虽然指针本身可以是可变的,但是它指向的对象不能是可变的。换句话说,不能声明"指向可变对象的指针"。

  • 简单类型,如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。

  • 具有以下基本类型之一的 enum 类型:byte、sbyte、short、ushort、int 或 uint。

  • 已知为引用类型的泛型类型参数。

  • IntPtr 和 UIntPtr。

其他类型(包括 double 和 long)无法标记为 volatile,因为对这些类型的字段的读取和写入不能保证是原子的。若要保护对这些类型字段的多线程访问,请使用 Interlocked 类成员或使用 lock 语句保护访问权限。

volatile 关键字只能应用于 class 或 struct 的字段。不能将局部变量声明为 volatile。

Part2

volatile并不能用来做线程同步,它的主要作用时为了让多个线程之间能看到被修改过后最新的值。

Part3

C#不支持以传递引用的方式将volatile字段传给方法。

int.TryParse("123", out x);

Part4

除了禁止编译优化,还有同步到内存中因为CPU每个核心都有自己Cache所以需要同步到内存中方便其他核心使用。

Part5

看完本文也能解开小白时期的疑惑,为什么我写代码编译成release版本之后就不能运行报错的奇特现象了。

Part6

volatile 牵扯到的相关知识点和原理远远不止这些。

"C#中Volatile的使用方法有哪些"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

线程 类型 编译 代码 方法 字段 静态 关键 关键字 内存 变量 编译器 存储 C# 安全 内容 学校 版本 知识 缓存 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 亿人帮互联网科技有限公司 广州淘业互联网络科技 互联网是第几次科技革命的成果 致聘互联网科技 互联网高科技股票 科技大学互联网专业好不好 哈尔滨秒房互联网科技有限公司 互联网公司会议桌创意科技软装饰 成都玖崟互联网科技有限公司乐商圈怎么样 创业天下互联网科技有限公司 银川新华互联网科技学校联系电话 翼展科技 首家互联网医院 众安科技工业互联网合作 张召忠谈中国互联网科技 有人说阿里系是互联网科技企业 云南互联网科技学费 长沙渺峰互联网科技有限公司 玉溪新华互联网科技怎么选 恒生互联网科技趋势 颐高云石互联网科技 互联网下的科技发展 互联网金融 科技金融 唐信互联网科技 大连 融贸通互联网科技 华中科技有哪些互联网大佬 互联网科技产业特征 济南有互联网科技公司吗 国大互联网教育科技有限公司薪资 湖北辰鑫科技互联网 银川新华互联网科技学校是国营吗
0