0

最近、ウォールクロックを参考にして25ミリ秒待機する待機関数を作成しようとしています。私は周りを見回して「gettimeofday」を見つけましたが、私はそれで問題を抱えていました。私のコード(簡略化):

while(1)
{
    timeval start, end;
    double t_us;
    bool release = false;    
    while (release == false)
    {
        gettimeofday(&start, NULL);

        DoStuff();

        {
            gettimeofday(&end, NULL);
            t_us = ( (end.tv_sec - start.tv_sec) * 1000*1000) + (end.tv_usec - start.tv_usec);

            if (t_us >= 25000) //25 ms
            {
                release = true;
            }
        }
    }
}

このコードはスレッド (Posix) で実行され、単独で正常に動作します。DoStuff() は 25ms ごとに呼び出されます。ただし、可能な場合はすべての CPU を消費するため (ご想像のとおり)、明らかにこれは良い考えではありません。

Sleep(1); を追加して調整しようとしたとき。if ステートメントの後の待機ループでは、全体が約 50% 遅くなります (つまり、約 37 ミリ秒ごとに DoStuff が呼び出されます。これは私には意味がありません。DoStuff と他のスレッドが (25 - 1) ミリ秒 DoStuff の呼び出されたレートは影響を受けません (1 ミリ秒のエラー マージンを許容)

Sleep(0)、usleep(1000)、usleep(0) も試しましたが、動作は同じです。

優先順位の高い別のスレッドが CPU 時間を必要とするときはいつでも、同じ動作が発生します (スリープなし)。スレッドがランタイムを放棄すると、時計のカウントが停止するかのようです。

gettimeofday は NTP の更新などに対して脆弱であることを認識しています。

誰かが私が間違っていることを知っていますか?

4

2 に答える 2

2

ここで欠けている部分は、カーネルがタイム スライスに基づいてスレッド スケジューリングを行う方法です。大まかに言えば、タイム スライスの開始時に 1 ミリ秒スリープし、スケジューリングが 35 ミリ秒のクロック レートで行われる場合、スレッドは 35 ミリ秒再実行されない可能性があります。40 ミリ秒スリープすると、スレッドは 70 ミリ秒再実行されない可能性があります。スケジューリングを変更せずに実際に変更することはできませんが、システムの全体的なパフォーマンスに影響するため、お勧めできません。「高解像度」タイマーを使用することもできますが、多くの場合、「まだ時間ではありませんが、CPU を噛む」というサイクルを浪費するタイトなループで実装されているため、これもあまり望ましくありません。

高解像度のクロックを使用し、DoStuff ループ内で頻繁にクエリを実行した場合、30 ミリ秒実行してから sleep(1) を実行するなどのトリックを実行して、タイムスライスの残りのスレッドを効果的に放棄することができます (例: 5ms) 他のスレッドを実行できるようにします。必要に応じて、協調的/先制的なマルチタスキングのようなものです。それでも、しばらくの間、仕事に復帰できない可能性があります...

于 2012-10-17T13:11:45.670 に答える
1

sleep()/usleep() のすべてのバリアントには、CPU を他の実行可能なタスクに譲ることが含まれます。その後、プログラムはカーネルによって再スケジュールされた後にのみ実行できます。これは、あなたの場合、約 37 ミリ秒続くようです。

于 2012-10-17T11:56:11.310 に答える