引用赋值是primefaces,所以为什么Interlocked.Exchange(ref Object,Object)需要?
在我的multithreadingasmx Web服务中,我有一个我自己types的SystemData的类字段_allData,它由less数List<T>
和Dictionary<T>
标记为volatile
。 系统数据( _allData
)刷新一次,我通过创build另一个名为newData
对象,并用新的数据填充它的数据结构。 完成后,我只是分配
private static volatile SystemData _allData public static bool LoadAllSystemData() { SystemData newData = new SystemData(); /* fill newData with up-to-date data*/ ... _allData = newData. }
这应该工作,因为赋值是primefaces的,并且对旧数据的引用的线程继续使用它,其余的在赋值之后有新的系统数据。 然而,我的同事说,而不是使用volatile
关键字和简单的assigment,我应该使用InterLocked.Exchange
因为他说,在某些平台上,不能保证参考分配是primefaces。 而且:当我声明the _allData
字段为volatile
Interlocked.Exchange<SystemData>(ref _allData, newData);
产生警告“对易失性字段的引用不会被视为易失性”我应该怎么看待这个问题?
这里有很多问题。 一次考虑一个:
引用赋值是primefaces,所以为什么Interlocked.Exchange(ref Object,Object)需要?
参考分配是primefaces的。 Interlocked.Exchange不会仅执行引用分配。 它读取一个variables的当前值,存储旧值,并将新值赋给variables,全部作为一个primefaces操作。
我的同事说,在一些平台上,不能保证参考分配是primefaces的。 我的同事是对的吗?
不可以。所有.NET平台上的引用赋值都是primefaces的。
我的同事是从错误的前提推理的。 这是否意味着他们的结论是不正确的?
不必要。 你的同事可能会给你很好的build议,原因很糟糕。 也许还有一些其他的原因,你应该使用Interlocked.Exchange。 无锁编程是非常困难的,当你离开这个领域的专家所熟知的行之有效的做法时,你就会脱离杂草,冒着最恶劣的竞争条件冒险。 我既不是这个领域的专家,也不是你的代码的专家,所以我不能以这样或那样的方式做出判断。
产生警告“对易失性字段的引用不会被视为易失性”我应该怎么看待这个问题?
你应该明白为什么这是一个普遍的问题。 这将导致理解为什么警告在这个特殊情况下是不重要的。
编译器给出这个警告的原因是因为将一个字段标记为volatile意味着“该字段将在多个线程上更新 – 不会生成任何代码来caching该字段的值,并确保任何读取或写入这个字段并不是“通过处理器caching不一致”而是“及时向前和向后移动”。
(我假设你已经理解了所有这些,如果你对volatile的含义没有详细的理解,以及它如何影响处理器caching语义,那么你就不知道它是如何工作的,不应该使用volatile。是很难得到正确的;确保你的程序是正确的,因为你了解它是如何工作的,而不是偶然的。)
现在假设你通过传递一个ref到这个字段来创build一个variables,它是一个volatile字段的别名。 在被调用的方法中,编译器没有任何理由知道引用需要具有易失性语义! 编译器会高兴地为无法实现易失性字段规则的方法生成代码,但该variables是一个易失性字段。 这可以完全破坏你的无锁逻辑; 总是假设易失性字段总是以易失性语义来访问。 把它看作是有时而不是其他时候是不稳定的, 你必须始终保持一致,否则你不能保证其他访问的一致性。
因此,当你这样做的时候,编译器会发出警告,因为它可能会彻底搞乱你精心开发的无锁逻辑。
当然,Interlocked.Exchange 是为了期望一个易变的领域而写的,并且做正确的事情。 这个警告因此是误导性的。 我非常后悔, 我们应该做的是实现一些机制,像Interlocked.Exchange这样的方法的作者可以把一个属性放在方法上说:“这个方法需要一个引用强制variables的volatile语义,所以压制警告”。 也许在未来版本的编译器中,我们应该这样做。
要么你的同事是错误的,要么他知道C#语言规范没有的东西。
5.5variables引用的primefaces性 :
“读取和写入以下数据types是primefaces的:bool,char,byte,sbyte,short,ushort,uint,int,float和referencetypes。
因此,您可以写入易失性参考,而不会有损坏值的风险。
当然,您应该小心如何确定哪个线程应该获取新的数据,从而最大限度地减less一次有多个线程执行此操作的风险。
Interlocked.Exchange <T>
将指定typesT的variables设置为指定值,并返回原始值,作为primefaces操作。
它改变并返回原始值,因为你只是想改变它而没用,正如Guffa所说,它已经是primefaces的了。
除非一个profiler被certificate是应用程序中的一个瓶颈,否则你应该考虑解锁,这样更容易理解并certificate你的代码是正确的。
Iterlocked.Exchange()
不仅仅是primefaces的,它还处理内存可见性:
以下同步function使用适当的屏障来确保内存sorting:
进入或离开关键部分的function
信号同步对象的函数
等待function
互锁function
同步和多处理器问题
这意味着,除了primefaces性,它确保:
- 对于调用它的线程:
- 没有对指令进行重新sorting(通过编译器,运行时或硬件)。
- 对于所有主题:
- 在这条指令之前没有对内存进行读操作会看到这条指令的改变。
- 这条指令之后的所有读操作都将看到这条指令所做的更改。
- 在指令更改到达主存储器之后,所有写入存储器的指令都会发生(当完成时将该指令更改到主存储器,而不让硬件刷新它自己的开启定时)。