using System; using System.Linq; using System.Collections.Generic; using System.Collections.Concurrent; using System.Reflection; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; State current = new(0); int notices = 0, attempts = 0; void Increment() { for (int item = 0; item < 50; item++) { while (true) { State before = Volatile.Read(ref current); var next = new State(checked(before.Count + 1)); Interlocked.Increment(ref attempts); if (!ReferenceEquals(Interlocked.CompareExchange(ref current, next, before), before)) continue; Interlocked.Increment(ref notices); break; } } } OwnedThreads.Run(Increment, Increment, Increment, Increment); Console.WriteLine($"current:{current.Count}"); Console.WriteLine($"committed-effects:{notices}"); Console.Error.WriteLine($"candidate-attempts:{attempts}"); sealed record State(int Count); public static class OwnedThreads { public static void Run(params Action[] actions) { var errors = new ConcurrentQueue(); var threads = actions.Select(action => new Thread(() => { try { action(); } catch (Exception error) { errors.Enqueue(error); } }) { IsBackground = true }).ToArray(); foreach (var thread in threads) thread.Start(); foreach (var thread in threads) if (!thread.Join(TimeSpan.FromSeconds(8))) throw new TimeoutException("Owned worker did not finish"); if (!errors.IsEmpty) throw new AggregateException(errors); } }