4

数年前にC++(MFC、Visual Studio 6.0)で構築され、特定のWindowsマシンでかなり長い間(5年以上)実行されているプログラムがあります。PCは1か月前に交換され(古いPCは故障しました)、それ以降、プログラムのタイミング動作が変更されました。理由を理解するのに助けが必要です。

プログラムの主な機能は、ON信号とOFF信号を外部カードに送信することでキーストロークに応答し、ONとOFFの間の遅延を非常に正確にすることです。プログラムフローの例:

> wait for keystroke...
> ! keystroke occurred
> send ON message
> wait 150ms
> send OFF message

キーストロークが異なれば、それに関連する待機時間も異なり、20ミリ秒から150ミリ秒の間です(特定のキーストロークに応じて非常に決定的な時間)。タイミングは非常に重要です。待機はsimpleを使用して実行されSleep()ます。古いPCでのスリープの精度は、1〜2ミリ秒の偏差でした。コンピューターの外部(外部カード上)でタイミングを測定できるので、スリープ時間の測定は非常に正確です。このようなON-sleep-OFFサイクルを何年にもわたって1日に何千回も実行したこのマシンを考慮に入れてください。私が持っている精度データは健全です。

PCを交換したため、タイミング偏差は10ms以上です。

以前のPCをインストールしなかったため、追加のソフトウェアパッケージがインストールされている可能性があります。また、以前のPCがWindows2000であったかWindowsXPであったかを覚えていないことを認めるのは恥ずかしいことです。XPだったと確信していますが、100%ではありません(今は確認できません...)。新しいものはWindowsXPです。

スリープメカニズムをタイマーベースに変更してみましたが、精度が向上しませんでした。

この変化を説明できるものはありますか?問題を解決する可能性のある以前のPCにインストールされている可能性のあるソフトウェアパッケージはありますか?問題に対処するためのベストプラクティスはありますか?

4

6 に答える 6

3

XP の時間分解能は約 10 ミリ秒です。システムは基本的に 10 ミリ秒ごとに「刻み」ます。そのため、スリープは正確なタイミングを行うにはあまり良い方法ではありません。win2000 の解像度が同じであると確信していますが、間違っている場合はそれが原因である可能性があります。

その解像度を少なくとも 1ミリ秒まで変更できますレジストリ キーも同様です (Windows メディア プレーヤーもそのタイマーを変更しますが、おそらく実行中のみです。

古いマシンで解像度が何らかの形で変更された可能性があります。

于 2009-09-12T19:51:46.770 に答える
2

精度が主な関心事である場合は、 の使用を検討してspinlockください。関数は、スケジューラが指定されたスレッドを少なくともxSleep()ミリ秒再スケジュールしないようにするためのヒントです。スレッドが指定された時間正確にスリープするという保証はありません。

于 2009-09-12T19:43:20.343 に答える
1

通常、Sleep()は、スリープ値に応じて、最大15ミリ秒の遅延、または最大15ミリ秒の倍数の期間になります。それがどのように機能するかを知る良い方法の1つは、次の擬似コードです。

while true do
    print(GetTickCount());
    Sleep(1);
end;

また、このコードの動作が、たとえばWindowsXPとVista/Win7で異なることも示されます。

于 2009-09-12T21:54:18.333 に答える
1

他の人が述べているように、睡眠は粗い精度を持っています。

私は通常、この種のタイミングにBoost::asioを使用します。

// Set up the io_service and deadline_timer
io_service io_
deadline_timer timer(io_service);

// Configure the wait period
timer.expires_from_now(posix_time::millisec(5));
timer.wait();

Asioは、プラットフォームに最も効果的な実装を使用します。Windowsでは、オーバーラップしたIOを使用していると思います。

期間を1msに設定し、「タイマー」をループするとします。継続時間の10000倍の呼び出しは、通常、約10005〜10100ミリ秒です。非常に正確で、クロスプラットフォームのコード(Linuxでは精度が異なります)で、非常に読みやすいです。

以前のPCがなぜそれほど正確だったのか説明できません。私がそれを使用したときはいつでも睡眠は+/-10msでした-PCが忙しい場合はさらに悪いです。

于 2009-09-13T05:33:55.600 に答える
0

スリープはシステム クロックに依存します。新しいマシンは、以前のマシンとはタイミングが異なる可能性があります。ドキュメントから:

この関数により、スレッドはタイム スライスの残りを放棄し、dwMilliseconds の値に基づく間隔で実行できなくなります。システム クロックは一定の速度で「カチカチ」音をたてます。dwMilliseconds がシステム クロックの分解能よりも小さい場合、スレッドは指定された時間より短い時間だけスリープする可能性があります。dwMilliseconds が 1 ティックより大きく 2 未満の場合、待機時間は 1 ティックから 2 ティックまでの間のいずれかになります。スリープ間隔の精度を上げるには、timeGetDevCaps 関数を呼び出して、サポートされている最小タイマー解像度を決定し、timeBeginPeriod 関数を呼び出してタイマー解像度を最小値に設定します。timeBeginPeriod を呼び出すときは注意してください。頻繁に呼び出すと、システム クロック、システムの電力使用量、およびスケジューラに大きな影響を与える可能性があります。

ドキュメントは、より正確にしようとすることができることを暗示しているようですが、私があなただったらそうはしません。タイマーを使うだけ。

どのタイマーに交換しましたか?SetTimer() を使用した場合、そのタイマーも最悪です。
正しい解決策は、高解像度のTimerQueueTimerを使用することです。

于 2009-09-12T19:33:56.633 に答える
0

新しい PC はマルチコアで、古い PC はシングルコアですか? タイミングの精度の違いは、複数のスレッドの使用とコンテキストの切り替えにある可能性があります。

于 2009-09-12T19:39:09.853 に答える