こまごまとした説明は省略しますが、Windowsの標準状態でのタイマー精度は15~16msec程度なので、Sleep(1)で1msecきっちりスリープ出来ると思ってはいけません。
timeBeginPeriod APIを呼び出すと精度を1msecまで改善する事が可能ですが、スレッドスケジューラに影響を与えるため、システム全体の負荷は上昇します。
あと、Sleepを全く挟まずに無限ループするとCPUリソースを使い果たす為、環境によっては熱暴走の危険もあります。
timeBeginPeriod(1) で精度を上げた後に、ループ毎に経過時間を計測し、その時点で経過していなければならない時間との差を取って、そのギャップ分Sleepする、みたいな事をすれば、500回に収める事が出来そうな気がします。
using System;
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class Program
{
[DllImport("winmm.dll")]
public static extern int timeBeginPeriod(int period);
[DllImport("winmm.dll")]
public static extern int timeEndPeriod(int period);
private static void UnknownJob()
{
Thread.SpinWait(1);
}
public static void Main(string[] args)
{
const int maxCount = 500;
// タイマー精度を1msecに上げる
timeBeginPeriod(1);
var sec = 0;
var count = 0;
var watch = Stopwatch.StartNew();
while (sec < 10)
{
UnknownJob();
// 1000/500なので、ループ毎に2msec経過しなければならない
// 実際の経過時間とのギャップが1msec以上ある時、その分をSleep
var elapsedMsec = watch.ElapsedMilliseconds;
var diffTime = count * 2 - elapsedMsec;
if (diffTime >= 1)
{
Thread.Sleep((int)diffTime);
}
count++;
if (count == maxCount)
{
watch.Stop();
Console.WriteLine($"経過時間:{watch.ElapsedMilliseconds} msec");
count = 0;
watch.Restart();
sec++;
}
}
// タイマー精度を戻す
timeEndPeriod(1);
}
}
大体こんな感じでしょうか。