Skip to main content

Example using Interlocked CompareExchange with retry support.

using System.Threading;
using System.Threading.Tasks;

namespace MyProgram
{
    class Program
    {
        static int Main(string[] args)
        {
            int returnCode = 0;
            ThreadSafeParallelOperations();
            return returnCode;
        }

        private static double total = 0.00;

        private static void ThreadSafeParallelOperations()
        {
            Parallel.For(0, 10000, next =>
            {
                // grab the snapshot
                double current = total;

                System.Console.WriteLine(
                    "iteration snapshot (current/total): {0}/{1}", current, total);

                // make first attempt...
                if (!current.AlmostEquals(
                    Interlocked.CompareExchange(ref total, current + next, current), 0.0000001))
                {
                    System.Console.WriteLine(
                        "current value changed since last snapshot (current/total): {0}/{1}",
                        current, total);

                    // if we fail, go into a spin wait, spin,
                    // and try again until succeed
                    var spinner = new SpinWait();
                    do
                    {
                        spinner.SpinOnce();
                        current = total;

                        System.Console.WriteLine(
                            "a spin occured (current/total): {0}/{1}",
                            current, total);
                    }
                    while (!current.AlmostEquals(
                        Interlocked.CompareExchange(ref total, current + next, current), 0.0000001)
                    );
                }
            });

            System.Console.WriteLine("{0}", total);
        }
    }

    public static class Extensions
    {
        /// <summary>
        /// Safely compare doubles for eqaulity.
        /// </summary>
        /// <returns><c>true</c>, if equals was almosted, <c>false</c> otherwise.</returns>
        /// <param name="double1">Double1.</param>
        /// <param name="double2">Double2.</param>
        /// <param name="precision">Precision.</param>
        /// <remarks>
        /// Is it safe to check floating point values for equality to 0?:
        /// https://stackoverflow.com/a/485777</remarks>
        public static bool AlmostEquals(this double double1, double double2, double precision)
        {
            return (Math.Abs(double1 - double2) <= precision);
        }
    }
}