C# volatile关键字

本文探讨了C#中的volatile关键字,强调其不能保证线程安全,仅能确保字段的最新值可见。volatile适用于修饰字段,如引用类型、指针类型及特定数值类型。尽管volatile防止了编译器优化,但无法保证操作的原子性和有序性,因此仍需配合lock以实现线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文仅为个人参考众多文章后的理解,如有错误请指正,红色内容必看。

volatile使用:

	private volatile int intValue;

	private volatile List<string> strValues;

	private volatile FormWindowState style;

	private volatile IntPtr mainPtr;

	private void button8_Click(object sender, EventArgs e)
	{
		lock (this)
		{
			intValue++;
			strValues.Add("1");
			style = FormWindowState.Minimized;
			mainPtr = Handle;
		}
	}

volatile使用说明:

  • volatile无法保证线程安全!!!
  • volatile只能修饰字段,不能修饰属性、局部变量。
  • volatile可用于以下类型的字段
    1. 引用类型
    2. 指针类型(C#语言支持指针类型,在不安全的上下文中)
    3. 类型为sbyte、byte、short、ushort、int、uint、char、float和bool,并非是值类型。
    4. 已知为引用类型的泛型类型参数。
    5. 基类为byte、sbyte、short、ushort、int 或 uint的枚举类型(枚举默认继承自int类型,但可以枚举设置其基类)。
    6. IntPtr和UInPtr类型。
  • 多线程操作volatile修饰字段的代码片段应该使用lock,且提供给lock的对象在不同的代码片段中应该为同一个引用对象。

volatile原理说明:

  • MSDN说明:

volatile 关键字指示一个字段可以由多个同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。

  • MSDN翻译:

CLR为了效率会进行优化,会允许线程对变量进行本地缓存等优化操作,当线程操作完后回将变量回写到内存中。当多线程同时执行时会出现变量访问不一致情况,使用volatile可以避免这种优化,保证所有线程对变量的读写都在内存上,保证了所有处理访问该字段都是最新值。

volatile为什么不是线程安全

MSDN上有这么一句话:volatile 修饰符通常用于由多个线程访问但不使用 lock 语句对访问进行序列化的字段。

字段为了线程安全可以使用属性在set和get方法中采用lock语句对字段实现线程安全,使用了volatile只是对字段原子性和可见性进行了保证,但没有保证线程对字段的操作性是有序性。即使在set和get方法中使用lock,依然无法保证对num的修改是线程安全的,因为对num操作的代码依然是线程非安全的。

例如:A和B线程同时对volatile修饰的int类型变量num进行加1操作,当A线程读取num的值后B线程读取num的值后加1赋值给num,线程A对num也加1后赋值给num。虽然A和B读取的num值都是最新的,但是对num+1的操作无法保证原子性和有序性,B先赋值给num后成为num+1,A再次进行写入依然是num+1。

线程安全

原子性、可见性、有序性。

原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作。

可见性:一个线程对主内存的修改可以及时被其他线程观察到。

有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值