C# multi-threaded unsigned increment

You say you don't want to use lock for performance reasons - but have you tested it? An uncontested lock (which this is likely to be, by the sounds of it) is pretty cheap.

I generally go for "obviously correct" rather than "clever and possibly better performing" when it comes to threading (and in general, but especially for threading).

Benchmark your app with and without locking, and see whether you can even notice the difference. If locking makes a significant difference then sure, use cunning stuff. Otherwise, I'd just stick with a lock.

One thing you might want to do is use Interlocked.Increment with an int and just cast it when necessary to get a uint, like this:

using System;
using System.Reflection;
using System.Threading;

public class Test
{
    private static int count = int.MaxValue-1;

    public static uint IncrementCount()
    {
        int newValue = Interlocked.Increment(ref count);
        return unchecked((uint) newValue);
    }

    public static void Main()
    {
        Console.WriteLine(IncrementCount());
        Console.WriteLine(IncrementCount());
        Console.WriteLine(IncrementCount());
    }

}

Output:

2147483647
2147483648
2147483649

(In other words it wraps with no problems.)


If you really need the full range of an unsigned int (2^32 - 1) rather than a signed int (2^31 -1), you could cast to an int64 (there is an Interlocked.Increment overload that takes int64) and then cast back to an unsigned int.


Building on pre-kidney's answer, you can create your own helper class. Since the increment will work in the same way on a binary level, you can just change the type from unsigned to signed before incrementing with the Unsafe class:

using System.Runtime.CompilerServices;
using System.Threading;

public static class InterlockedEx
{
    /// <summary>
    /// unsigned equivalent of <see cref="Interlocked.Increment(ref Int32)"/>
    /// </summary>
    public static uint Increment(ref uint location)
    {
        int incrementedSigned = Interlocked.Increment(ref Unsafe.As<uint, int>(ref location));
        return Unsafe.As<int, uint>(ref incrementedSigned);
    }

    /// <summary>
    /// unsigned equivalent of <see cref="Interlocked.Increment(ref Int64)"/>
    /// </summary>
    public static ulong Increment(ref ulong location)
    {
        long incrementedSigned = Interlocked.Increment(ref Unsafe.As<ulong, long>(ref location));
        return Unsafe.As<long, ulong>(ref incrementedSigned);
    }
}