using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
public static class Program
public static void Main()
ThreadPool.SetMinThreads(10, 10);
var locker = new RangeLock<int>();
var stopwatch = Stopwatch.StartNew();
var workers = Enumerable.Range(1, 10).Select(n => Task.Run(async () =>
var random = new Random();
while (stopwatch.ElapsedMilliseconds < 1000)
int size = random.Next(0, data.Length / 3);
int start = random.Next(0, data.Length - size);
int end = start + size + 1;
var token = await locker.WaitAsync(start, end);
for (int i = start; i < end; i++) data[i]++;
for (int i = start; i < end; i++) data[i]--;
finally { locker.Release(token); }
Console.WriteLine($"Worker #{n} finished ({loops:#,0} loops)");
var totalLoops = Task.WhenAll(workers).Result.Sum();
var ok = data.All(x => x == 0);
Console.WriteLine($"All workers finished, Total loops: {totalLoops:#,0}, Validation: {(ok ? "OK" : "Error!")}");
if (!ok) Console.WriteLine($"Data: [{String.Join(", ", data)}]");
public class RangeLock<TBoundary>
public TBoundary Start { get; init; }
public TBoundary End { get; init; }
public TaskCompletionSource<object> TCS;
public List<Entry> Overlapped;
private readonly IComparer<TBoundary> _comparer;
private readonly List<Entry> _entries = new();
public RangeLock(IComparer<TBoundary> comparer = default)
_comparer = comparer ?? Comparer<TBoundary>.Default;
public Task<object> WaitAsync(TBoundary start, TBoundary end)
if (_comparer.Compare(end, start) < 0)
throw new ArgumentOutOfRangeException(nameof(end));
var entry = new Entry() { Start = start, End = end };
foreach (var older in _entries)
if (Overlaps(entry, older))
(older.Overlapped ??= new()).Add(entry);
if (entry.Countdown == 0) return Task.FromResult((object)entry);
entry.TCS = new TaskCompletionSource<object>(
TaskCreationOptions.RunContinuationsAsynchronously);
public object Wait(TBoundary start, TBoundary end)
=> WaitAsync(start, end).GetAwaiter().GetResult();
public void Release(object token)
if (token == null) throw new ArgumentNullException(nameof(token));
if (!(token is Entry entry)) throw new ArgumentException("Invalid token.");
if (!_entries.Remove(entry))
throw new ArgumentException("Unknown token.");
if (entry.Overlapped == null) return;
foreach (var overlapped in entry.Overlapped)
if (overlapped.Countdown == 0)
Debug.Assert(overlapped.TCS != null);
overlapped.TCS.SetResult(overlapped);
private bool Overlaps(Entry entry1, Entry entry2)
_comparer.Compare(entry1.Start, entry2.Start) <= 0 &&
_comparer.Compare(entry1.End, entry2.Start) > 0
_comparer.Compare(entry2.Start, entry1.Start) <= 0 &&
_comparer.Compare(entry2.End, entry1.Start) > 0