.net 易失字段

internal sealed class ThreadSharingData{
	private Int32 m_flag =0;
	private Int32 m_value =0;
	
	//由一个线程执行
	public void Thread1()
	{
		// 注意:以下两行代码可以按相反的顺序执行
		m_value =5;
		m_flag =1;
	}

	// 这个方法由另一个线程执行
	public void Thread2()
	{
		// 注意:m_value 可能在m_flag之前读取
		if(m_flag == 1)
		{
			Console.WriteLine(m_value);
		}
	}
}

上述代码的问题在于,编译器/CPU 在解释代码的时候,可能反转Thread1方法中的两行代码。毕竟,反转两行代码不会改变方法的意图。方法需要在m_value中存储5,在m_flag中存储1. 从单线程应用程序的角度来说,这梁行代码的执行顺序无关紧要。如果这两行代码真的按相反顺序执行。执行Thread2方法的另一个线程可能看到m_flag是1, 并显示0

下面从另一个角度研究上述代码。假定Thread1方法中的代码按照程序顺序执行。编译Thread2方法中的代码时,编译器必须生成代码将m_flag和m_value从RAM读入CPU寄存器。RAM可能先传递m_value的值,它包含一个0值。然后,Thread1方法可能执行,将m_value 更改为5,将m_flag更改为1 但是,Thread2的cpu 寄存器没有看到m_value已被另一个线程更改为5. 然后m_flag的值从RAM读入CPU寄存器。由于m_flag已变成1,所以造成Thread2同时显示0

这些细微之处很容易被人忽视。由于调试版本不会进行优化,所以等到程序生成发行版本的时候,这些问题才会显现出来,造成很难检测这些问题并纠正代码。现在,研究一下如何纠正自己的代码。

System.Threading.Thread 类提供了三个静态方法,如下所示:

public sealed class Thread {
	public static void VolatileWrite(ref Int32 address, Int32 value);
	public static Int32 VolatileRead(ref Int32 address);
	public static void MeoryBarrier();
}

这些方法比较特殊。事实上,这些方法会禁用C#编译器、JIT编译器和CPU自身所执行一些优化。下面描述了方法时如何工作的。
1, VolatileWrite 方法强迫address 中的值在调用时写入。除此以外,按照程序顺序,在之前的加载和存储操作必须在调用VolatileWrite之前发生。
2,VolatileRead 方法强迫address中的值在调用时读取。除此之外,按照程序顺序,在之后加载和存储操作必须在调用VolatileRead之后发生。
3,MemoryBarrier方法不访问内存,它强迫按照程序顺序,之前加载和存储操作在调用MemoryBarrier之前完成。榆次同时,之后的加载和存储操作在调用MemoryBarrier之后完成。和另外两个方法相比,MemoryBarrier的作用小得多。

internal sealed class ThreadSharingData{
	private Int32 m_flag =0;
	private Int32 m_value =0;
	
	//由一个线程执行
	public void Thread1()
	{
		// 注意: 在将1写入m_flag之前,必须先将5写入m_value
		m_value =5;
		Thread.VolatileWrite(ref m_flag, 1);
	}

	// 这个方法由另一个线程执行
	public void Thread2()
	{
		// 注意:m_value 必须在读取了m_flag之后读取
		if(Thread.VolatileRead(ref m_flag) == 1)
		{
			Console.WriteLine(m_value);
		}
	}
}

以上代码还可以用volatile 关键字 改写

internal sealed class ThreadSharingData{
	private volatile Int32 m_flag =0;
	private Int32 m_value =0;
	
	//由一个线程执行
	public void Thread1()
	{
		// 将1写入之前,必须先写入m_value
		m_value =5;
		m_flag =1;
	}

	// 这个方法由另一个线程执行
	public void Thread2()
	{
		// 注意:m_value 必须m_flag之后读取
		if(m_flag == 1)
		{
			Console.WriteLine(m_value);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值