当前位置: 首页 > 编程语言 > C#

Interlocked.CompareExchange使用GreaterThan或LessThan而不是相等共享

时间:2023-04-10 17:23:10 C#

Interlocked.CompareExchange使用GreaterThan或LessThan而不是相等System.Threading.Interlocked对象允许将加法(减法)和比较作为原语操作。似乎CompareExchange只是不做平等,而GreaterThan/LessThan作为基元比较似乎非常有价值。假设的Interlocked.GreaterThan是IL的特性还是CPU级函数?全部?如果没有任何其他选项,是否可以在C++或直接IL代码中创建这样的函数并将该函数公开给C#?在这里更新我后来的帖子:我们找到了一种更好的方法,即使用一个额外的锁对象进行更大的比较。我们编写了一些单元测试来验证锁和互锁可以一起使用,但只能在某些情况下使用。代码如何工作:使用读或写互锁是原语的内存屏障。大于比较的原语操作需要使用同步锁。所以现在的规则是这个类中没有其他操作在没有这个同步锁的情况下写入值。我们在这个类中得到的是一个环环相扣的值,读起来很快,但是写起来要费点功夫。在我们的应用程序中,读取速度大约快2-4倍。这是视图的代码:请参见此处:http://files.thekieners.com/blogcontent/2012/ExchangeIfGreaterThan2.png这是复制并粘贴的代码:publicsealedclassInterlockedValue{privatelong_myValue;私有只读对象_syncObj=newobject();publiclongReadValue(){//读取值(应用程序中99.9%的情况)不会使用锁对象,//因为这在我们高度多线程的应用程序中开销太大。返回Interlocked.Read(ref_myValue);}publicboolSetValueIfGreaterThan(longvalue){//同步Exchange对_myValue的访问,因为需要安全的大于比较lock(_syncObj){//大于条件if(value>Interlocked.Read(ref_myValue)){//现在我们可以将值保存为_myValue。环环相扣。交换(ref_myValue,价值);返回真;}返回假;您可以使用InterlockedCompareExchange构建其他原语操作。publicstaticboolInterlockedExchangeIfGreaterThan(refintlocation,intcomparison,intnewValue){intinitialValue;做{初始值=位置;如果(初始值>=比较)返回false;}while(System.Threading.Interlocked.CompareExchange(reflocation,newValue,initialValue)!=initialValue);返回真;您如何看待这个实现://这是一个Interlocked.ExchangeIfGreaterThan实现//比较while(current=value)//注意:最常见的情况是先断;//对于所有其他情况,我们需要另一次运行(读取值、比较、设置)current=Interlocked.Read(reflocation);这实际上不是真的,但将并发视为2种形式很有用:无锁并发基于锁的并发这不是真的,因为基于软件锁的并发最终会使用堆栈中的某处锁-free原语指令被实现(通常在内核中)。然而,无锁原语指令最终都会获得内存总线上的硬件锁。所以,实际上,无锁并发和基于锁的并发是一样的。但从概念上讲,在用户应用程序级别,它们是两种不同的做事方式。基于锁的并发基于“锁定”对代码关键部分的访问的思想。当一个线程“锁定”一个临界区时,没有其他线程可以在同一临界区内运行代码。这通常是通过使用与os调度程序交互的“互斥锁”来完成的,并导致线程在等待进入锁定的临界区时变得不可运行。另一种方法是使用“自旋锁”,它会导致线程在循环中自旋,在临界区可用之前什么都不做。无锁并发基于使用由硬件保证以原始方式运行的原始指令(CPU特别支持)的想法。Interlocked.Increment是无锁并发的一个很好的例子。它只是调用执行原语增量的特殊CPU指令。无锁并发很难。随着关键部分的长度和复杂性的增加,它变得特别困难。关键部分中的任何步骤都可以由任意数量的线程同时执行,并且它们可以以截然不同的速度移动。尽管如此,您必须确保结果对于整个系统都是正确的。对于增量之类的东西,它可以很简单(cs只是一条指令)。对于更复杂的关键部分,事情会变得非常复杂。基于锁的并发也很难,但没有无锁并发那么难。它允许您创建任意复杂的代码区域,并且知道在任何时候只有1个线程在执行它。无锁并发有一大优势:速度。如果使用得当,它可以比基于锁的并发快几个数量级。自旋循环对长时间运行的关键部分不利,因为它们会浪费CPU资源。互斥量对于小的关键部分可能是不利的,因为它们会引入大量开销。它们涉及最少的模式切换,以及最坏情况下的多个上下文切换。考虑实施托管堆。每次调用操作系统时都调用“new”是不好的。它会破坏你的应用程序的性能。然而,通过无锁并发,可以使用互锁增量来实现gen0内存分配(我不确定CLR是否这样做,但如果不是,我会感到惊讶。这可能是一个巨大的节省.还有其他的用途,比如无锁数据结构,比如持久栈和avl树,他们通常使用“cas”(比较和交换)。但是,基于锁的并发和无锁并发的原因实际上是等价的,因为每个实现细节都是真的。自旋锁通常在其循环条件中使用原语指令(通常是cas)。互斥体在其实现中需要自旋锁或内部内核结构的原语更新。Primefaces指令又使用硬件锁实现.不管怎样,它们都有一系列权衡,通常以性能与复杂性为中心。与无锁代码相比,互斥锁既可以更快也可以更慢。无锁代码可能比互斥锁更复杂。使用适当的机制取决于具体情况。现在,回答您的问题:如果小于调用者不使用锁定,则执行互锁比较交换的方法。您不能用一条指令以相同的方式实现增量或比较交换。您可以在循环中使用互锁比较交换来模拟它进行减法(计算小于)。您也可以使用互斥量(但这意味着锁定,因此在名称中使用“互锁”会产生误导)。是否适合构建“通过cas模拟互锁”版本?那要看。如果代码被频繁调用,并且很少有线程争用,那么答案是肯定的。如果不是,您可以将具有适度高常数因子的O(1)操作变成无限(或非常长)循环,在这种情况下最好使用互斥锁。大多数时候这是不值得的。使用这些辅助方法,您不仅可以交换值,还可以检测它是否被替换。用法如下:intcurrentMin=10;//可以随时从其他线程更改intpotentialNewMin=8;if(InterlockedExtension.AssignIfNewValueSmaller(refcurrentMin,potentialNewMin)){Console.WriteLine("新最小值:"+potentialNewMin);}这是方法:publicstaticclassInterlockedExtension{publicstaticboolAssignIfNewValueSmaller(refinttarget,intnewValue){intsnapshot;布尔仍然更少;做{快照=目标;stillLess=newValue快照;}while(stillMore&&Interlocked.CompareExchange(reftarget,newValue,snapshot)!=snapshot);返回更多;}}硬件直接支持所有联锁操作。互锁操作和原始数据类型是不同的东西。Primefaces类型是库级别的特征。在某些平台上,对于某些数据类型,原语是使用互锁指令实现的。在这种情况下,它们非常有效。在其他情况下,当平台根本没有互锁操作或某些特定数据类型不可用时,库会使用适当的同步(crit_sect、mutex等)来实现这些操作。我不确定是否真的需要Interlocked.GreaterThan。否则它可能已经实现了。如果你知道一个很好的例子,我相信在座的每个人都会很高兴听到这个消息。大于/小于或等于已经是原始操作。这不会解决您的应用程序的安全并发行为。让他们成为一个环环相扣的家庭的一部分是没有意义的,所以问题是:你到底想达到什么目的?以上是C#学习教程:Interlocked.CompareExchange使用GreaterThan或LessThan代替均分。如果对大家有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场,如涉及侵权,请点击右边联系管理员删除。如需转载请注明出处: