using System.Collections.Generic;
using System.Diagnostics;
public static void Main()
Console.WriteLine($"Environment.ProcessorCount: {Environment.ProcessorCount}");
Console.WriteLine($"Environment.OSVersion: {Environment.OSVersion}");
const int threadsCount = 10;
TimeSpan testDuration = TimeSpan.FromMilliseconds(200);
TimeSpan idleThreshold = TimeSpan.FromMilliseconds(0.1);
Console.WriteLine($"Test threads: {threadsCount}, test duration {testDuration.TotalMilliseconds:#,0} msec, idle threshold {idleThreshold.TotalMilliseconds:#,0.00} msec");
Result[] results = new Result[threadsCount];
Thread[] threads = Enumerable.Range(0, threadsCount).Select(i => new Thread(() =>
Stopwatch stopwatch = Stopwatch.StartNew();
TimeSpan sliceStart = stopwatch.Elapsed;
TimeSpan previous = sliceStart;
List<Slice> slices = new();
TimeSpan current = stopwatch.Elapsed;
if (current > testDuration) break;
if (current - previous > idleThreshold)
slices.Add(new(previous - sliceStart, current - previous));
results[i] = new(Thread.CurrentThread.ManagedThreadId, slices.ToArray(), loops);
Array.ForEach(threads, t => t.Start());
Array.ForEach(threads, t => t.Join());
foreach (Result result in results)
Console.WriteLine($"Thread #{result.Id} slices:");
foreach (var (running, sleeping) in result.Slices)
Console.WriteLine($"- Running: {running.TotalMilliseconds:#,0.00} msec, Sleeping: {sleeping.TotalMilliseconds:#,0.00} msec");
Console.WriteLine($"- Running AVG: {TimeSpan.FromMilliseconds(result.Slices.Select(e => e.Running.TotalMilliseconds).Average()).TotalMilliseconds:#,0.00} msec, Sleeping AVG: {TimeSpan.FromMilliseconds(result.Slices.Select(e => e.Sleeping.TotalMilliseconds).Average()).TotalMilliseconds:#,0.00} msec, Loops: {result.Loops:#,0}");
private record struct Slice(TimeSpan Running, TimeSpan Sleeping);
private record struct Result(int Id, Slice[] Slices, int Loops);