System.Threading.Interlocked.CompareExchange
operator provides atomic (thus thread-safe) C# implementation of the Compare-And-Swap operation.
For example int i = 5; Interlocked.CompareExchange(ref i, 10, 5);
After this command, the int i would have a value = 10. And also the compare and exchange happens atomically (single operation).
When I tried using this with a class instance, the compare fails and the values are not exchanged.
public class X
{
public int y;
public X(int val) { y = val; }
}
Now when I do
X a = new X(1);
X b = new X(1);
X c = new X(2);
Interlocked.CompareExchange<X>(ref a, c, b);
The compare and Exchange operation fails. So, I overrided the Equals and the == operator for the class X as
public override bool Equals(object obj) { return y == ((X) obj).y; }
So, now I get Interlocked.Equals(a,b)
as true
, but the CompareExchange
operations still fails.
Is there any method to do this? I want to compare two class instances and assign one of them a value based on the comparision.
No. It can't be done.
Interlocked.CompareExchange
basically maps directly to an assembly instruction which is able to atomically compare and swap the contents of a memory address. I believe in 32-bit mode, a 64-bit version of the instruction is available (as well as 32- and 16-bit versions), and in 64-bit mode, I think a 128-bit version is available. But that's all. The CPU doesnt' have a "swap .NET class based on its specific Equals
function" instruction.
If you want to swap arbitrary objects, using arbitrary equality functions, you have to do it yourself, using locks or other synchronization mechanisms.
There is an overload of the Interlocked.CompareExchange
function which works on object references, but it uses reference equality for the above reason. It simply compares the references, and then swaps them.
In response to your comment, using structs would not solve the problem. Again, the CPU can only atomically compare and swap values of certain fixed sizes, and it has no notion of abstract datatypes. Reference types can be used because the reference itself has a valid size, and can be compared against another reference by the CPU. But the CPU knows nothing about the object that the reference points to.