私はCで書かれたゲームサーバーを開発しています。アルゴリズムを実行するには、特定の頻度(1秒あたり50回)のサイクルを開発する必要があります。問題は、プログラムを正確な時間間隔-20000マイクロ秒で一時停止できないことです。関数usleep(20000)
は約30000マイクロ秒実行します。結果は常に予想よりも10000マイクロ秒多くなります。
これが私のコードの簡単な例です:
_#include <stdio.h>
#include <time.h>
#include <unistd.h>
int main( int argc, char ** argv )
{
const unsigned long long nano = 1000000000;
unsigned long long t1, t2;
struct timespec tm;
for(;;)
{
clock_gettime( CLOCK_REALTIME, &tm );
t1 = tm.tv_nsec + tm.tv_sec * nano;
usleep( 20000 );
clock_gettime( CLOCK_REALTIME, &tm );
t2 = tm.tv_nsec + tm.tv_sec * nano;
printf( "delay: %ld\n", ( t2 - t1 ) / 1000 );
}
return 0;
}
_
そしてそれが実行されている結果:
_$ ./a.out
delay: 29233
delay: 29575
delay: 29621
delay: 29694
delay: 29688
delay: 29732
delay: 29709
delay: 29706
delay: 29666
delay: 29702
delay: 29702
delay: 29705
delay: 29789
delay: 29619
delay: 29785
delay: 29634
delay: 29692
delay: 29708
delay: 29701
delay: 29703
_
関数select()
も使用しようとしましたが、結果はsleep()
と同じです。
私のコードのどこが悪いのか説明してください。
pS:
_$ uname -a
FreeBSD home.com 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Tue Jan 3 07:46:30 UTC 2012 [email protected]:/usr/obj/usr/src/sys/GENERIC AMD64
_
20000マイクロ秒スリープする代わりに、clock_gettimeの呼び出しに基づいて、再度実行するまでの残り時間スリープします。
つまり:
usleep( lasttime+20000-now ); // But make sure you don't sleep when the result is negative
コードに問題があるわけではありませんが、実際のスリープの呼び出し、時間の読み取りなどには時間がかかり、システムは正確なクロックサイクルの倍数でない限り、とにかく正確な時間スリープできません。
非リアルタイムシステムのスリープ関数は、指定された正確な期間のスリープを保証していません。ビジー状態のシステムでは、プロセスはそのタイムスライスが開始したときにのみ起こされます。または、 マニュアルページに記載されているとおり 、「システムアクティビティはスリープを不確定な量だけ長くする可能性があります」。
kern.hz
周波数が100に下げられたように、10msに近い量の音が聞こえます VM setups の推奨として)。
この問題の典型的な回避策は、Ofirが提供するものです。固定のスリープ間隔を指定する代わりに、残りのスリープ時間を指定します。平均して、ループは20ミリ秒ごとに実行されます。これは、ほとんどの場合達成したいことです。