問題を裏返します。ループを1秒間にN回に制限しないでください。代わりに、N個の作業単位を目的の時間に均等に分散して処理します。
つまり、開始(または前の作業)から経過した時間を計算し、それを作業率に補間して、その量の作業を実行します(開始/前の時間と実行された作業の量を考慮に入れます)。これは、多くのゲーム/アニメーションエンジンの基本的な基盤である「デルタタイム」です。
次にyield
、各ループの最後で「良くなる」ように呼び出します。つまり、99%以上のCPU使用率を消費しないようにします。歩留まり自体の分解能は最小1ですが、特に適切に補間する場合、効果は一般的に適切です。
補間アプローチが使用されるため、これは、ループごとにさらに多くのNを実行することを意味する場合でも、すべてのN(割り当てられた時間内に実行可能)で機能するはずです。小さなNに対して特定のループで作業が行われない可能性もありますが、yield
この種の「非常にビジーなループ」はCPU使用率の点で安価になります2。
これは、1秒間に20個の「x」を出力するための擬似コードです。ここで、now
秒の小数部が返されます。
rate = 20 // per second - "N times per second"
done = 0
goal = 1 * rate // same as rate for 1 second
start = now()
while done < goal:
target = floor((now() - start) * rate)
work = (target - done) // work might be 0, that's okay
for 0 upto work:
print("x")
done += work
yield()
この場合、定率式のため、開始時刻から簡単に補間できます。最後の作業(またはループ)からの時間に基づく「デルタ時間」の使用は類似しており、離散式がない場合に適していますが、少し複雑で、微妙なドリフトエラーが発生する可能性があります。
1実際 の時間分解能sleep/yield
は実装に依存し、システムによって異なります。たとえば、Linuxの場合は1ミリ秒、Windowsの場合は10〜15ミリ秒の範囲である可能性があります。
2タイムデルタを処理することに加えてsleep
、Dariusz Wawerの回答に従って、期間を変更できます。ただし、これにより複雑さが増し、yield
多くの場合、単純なもので十分です。