我觉得整个页面有些混乱。首先,评论员是正确的,该问题包含一个危险的假设:
int i = 5;
Interlocked.CompareExchange(ref i, 10, 5);
在此命令之后,int i 将具有值 = 10。
不,仅当 的值i
未更改为除此期间以外的值5
时。尽管这在此处显示的代码中似乎不太可能,但使用的全部意义CompareExchange
在于它应该是可能的,因此这是一个关键的技术性。我担心 OP 可能不理解 的目的Interlocked.CompareExchange
,特别是因为他没有检查返回值(见下文)。
现在原始问题的文本是:
“有什么方法可以做到这一点吗?我想比较两个类实例并根据比较为其中一个分配一个值。”
由于“this”这个词没有可行的先行词,我们或许应该将后面的句子视为问题,给出解释:
“有没有办法比较两个类实例并根据比较为其中一个分配一个值?”
不幸的是,这个问题仍然不清楚,或者可能与原子操作无关。首先,你不能“给 [一个类实例] 赋值”。这没有任何意义。对类实例的引用是一个值,但没有办法将任何东西“分配”给类实例本身。这是与可以相互分配的值类型的主要区别。您可以使用操作符创建一个实例,但您仍然只是获得对它的引用。同样,这些可能看起来像技术问题,但如果问题真的是关于无锁并发的关键点。new
接下来,该Interlocked.CompareExchange
函数不以 value 为条件存储位置,而是有条件地将值存储到(给定的) location,这意味着它要么存储值(成功),要么保持存储位置不变(失败),同时可靠地指出其中哪些发生了。
这意味着“基于比较”这个短语对于备选动作应该是什么是不完整的。查看 OP 问题的前面部分,一个最佳猜测可能是该问题正在寻求有条件地操纵实例引用,而原子性是一个红鲱鱼。很难知道,因为如上所述,CompareExchange
(用于说明问题)不会“交换”内存中的两个值,它只可能“存储”一个值。
X a = new X(1);
X b = new X(1);
X c = new X(2);
if (a.y == b.y)
a = c;
else
// ???
随着Equals
重载,这可以简化:
if (a == b)
a = c;
else
// ???
OP对内部领域平等的关注y
似乎增加了对问题的这种解释走在正确轨道上的可能性。但显然,这些问题的答案与Interlocked.CompareExchange
. 我们需要更多信息来了解为什么 OP 认为分配必须是原子的。
因此,或者,我们应该注意,也可以原子地交换y
现有实例中的值:
var Hmmmm = Interlocked.CompareExchange(ref a.y, c.y, b.y);
或者交换实例引用,现在应该很明显,相等引用仅根据“引用相等”定义:
var Hmmmm = Interlocked.CompareExchange(ref a, c, b);
要从这里开始,这个问题需要更加明确。例如,要重述本页其他地方的评论,但更强烈的是,不检查 Interlocked.CompareExchange 的返回值是错误的。
这就是我在上面的示例中存储返回值的原因,以及我认为它的名称是否合适的原因。不对返回值进行分支就是不了解无锁(“乐观”)并发的基本原理,对此的讨论超出了本问题的范围。有关出色的介绍,请参阅Joe Duffy在 Windows 上的并发编程。
最后,我认为 OP 不太可能真的需要基于任意考虑以原子方式存储类引用,因为这是一个非常专业的操作,通常仅在全面的无锁系统设计的关键时刻才需要。但是(与另一个答案相反)这当然可以按照@supercat 描述的方式进行。
所以请不要以为你不能在 .NET 中编写无锁代码,或者类引用对Interlocked
操作有任何问题;实际上恰恰相反:如果您确实需要在两个不同的存储位置之间进行选择或以其他方式影响多个内存位置的原子操作,则使用将纠缠的位置包装在一个琐碎的包含类中的设计很简单,然后为您提供可以以无锁方式原子交换的单个引用。无锁编码在 .NET 中轻而易举,因为在乐观路径失败的极少数情况下,它可以减少内存管理重试对象的麻烦。
可以这么说,根据我的经验,在C#/.NET/CLR中没有我无法实现的无锁并发的基本方面,即使它有时在边缘有点粗糙,你可能会从https://stackoverflow.com/a/5589515/147511确定。