using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
public static class Program
public static void Main()
Debug.Assert(false, "Debug built");
const int workersCount = 8;
const int durationOfEachTest = 1500;
const int batchOperations = 1000;
ThreadPool.SetMinThreads(workersCount, workersCount);
Console.WriteLine($"Workers: {workersCount}, " +
$"ProcessorCount: {Environment.ProcessorCount}");
Console.WriteLine($"Duration of each test: {durationOfEachTest} msec, " +
$"Batch enqueue/dequeue: {batchOperations:#,0}");
Test(null, new ConcurrentQueue<int>(), 100);
Test(null, new MyConcurrentQueue<int>(), 100);
Test("ConcurrentQueue", new ConcurrentQueue<int>(), durationOfEachTest);
Test("Queue+Lock", new MyConcurrentQueue<int>(), durationOfEachTest);
static void Test(string title, IProducerConsumerCollection<int> queue,
if (title != null) Console.WriteLine();
if (title != null) Console.WriteLine($"{title}");
GC.WaitForPendingFinalizers();
var lcc0 = Monitor.LockContentionCount;
var mem0 = GC.GetTotalAllocatedBytes(true);
var stopwatch = Stopwatch.StartNew();
var workers = Enumerable.Range(1, workersCount).Select(n => Task.Run(() =>
while (stopwatch.ElapsedMilliseconds < durationMsec)
for (int i = 0; i < batchOperations; i++) queue.TryAdd(i);
for (int i = 0; i < batchOperations; i++) queue.TryTake(out _);
return outerLoops * batchOperations * 2;
long totalLoops = Task.WhenAll(workers).Result.Sum();
var mem1 = GC.GetTotalAllocatedBytes(true);
var lcc1 = Monitor.LockContentionCount;
if (title != null) Console.WriteLine(
$"Total operations per second: {(totalLoops * 1000L) / durationMsec:#,0}");
if (title != null) Console.WriteLine(
$"Allocated: {(mem1 - mem0) * 1000L / durationMsec:#,0} bytes per second, " +
$"Lock contention per second: {(lcc1 - lcc0) * 1000L / durationMsec:#,0}");
if (queue.Count > 0) throw new InvalidOperationException();
private class MyConcurrentQueue<T> : IProducerConsumerCollection<T>
private readonly Queue<T> _queue = new();
public bool TryAdd(T item)
lock (_queue) _queue.Enqueue(item); return true;
public bool TryTake(out T item)
if (_queue.Count == 0) { item = default; return false; }
item = _queue.Dequeue(); return true;
public int Count { get { lock (_queue) return _queue.Count; } }
public bool IsSynchronized => throw new NotImplementedException();
public object SyncRoot => throw new NotImplementedException();
public void CopyTo(T[] array, int index) => throw new NotImplementedException();
public void CopyTo(Array array, int index) => throw new NotImplementedException();
public IEnumerator<T> GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
public T[] ToArray() => throw new NotImplementedException();