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; var first = new Account(1, 1000); var second = new Account(2, 1000); OwnedThreads.Run(() => { for (int i = 0; i < 100; i++) Transfer(first, second, 1); }, () => { for (int i = 0; i < 100; i++) Transfer(second, first, 1); }); Console.WriteLine($"a:{first.Balance}"); Console.WriteLine($"b:{second.Balance}"); Console.WriteLine($"total:{first.Balance + second.Balance}"); static void Transfer(Account from, Account to, int amount) { if (amount < 1 || from.Id == to.Id) throw new ArgumentException("Distinct ordered accounts and positive amount required"); Account low = from.Id < to.Id ? from : to, high = from.Id < to.Id ? to : from; lock (low.Sync) lock (high.Sync) { if (from.Balance < amount) throw new InvalidOperationException(); from.Balance -= amount; to.Balance += amount; } } sealed class Account(int id, int balance) { public int Id { get; } = id; public object Sync { get; } = new(); public int Balance = balance; } 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); } }