61

Windowsでは、Unixでは発生したことのない問題が発生します。これが、スレッドを1ミリ秒未満スリープさせる方法です。Unixでは、通常、ニーズに合わせていくつかの選択肢(sleep、usleep、nanosleep)があります。ただし、Windowsでは、ミリ秒単位の粒度のスリープしかありません。

Unixでは、selectシステムコールを使用してマイクロ秒のスリープを作成できます。これは非常に簡単です。

int usleep(long usec)
{
    struct timeval tv;
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, 0, &tv);
}

どうすればWindowsで同じことを達成できますか?

4

18 に答える 18

88

これは、睡眠機能の誤解を示しています。渡すパラメーターは、スリープの最小時間です。指定された正確な時間後にスレッドがウェイクアップするという保証はありません。実際、スレッドはまったく「ウェイクアップ」するのではなく、OS スケジューラによって実行のために選択されます。スケジューラは、特にその時点で別のスレッドがまだアクティブである場合、スレッドをアクティブにするために、要求されたスリープ期間よりもはるかに長く待機することを選択する場合があります。

于 2008-09-17T16:40:21.543 に答える
48

Joel が言うように、そのような短い期間、意味のある「スリープ」(つまり、スケジュールされた CPU を放棄) はできません。少し遅らせたい場合は、適切に高解像度のタイマー(「パフォーマンスタイマー」など)を繰り返しチェックし、優先度の高い何かがあなたを先取りしないことを期待して、スピンする必要があります。

このような短い時間の正確な遅延を本当に気にする場合は、Windows を使用しないでください。

于 2008-09-17T16:45:57.973 に答える
34

winmm.lib で利用可能な高解像度マルチメディア タイマーを使用します。例については、これを参照してください。

于 2008-09-17T16:46:01.593 に答える
20
#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");

static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}

非常に短い時間の睡眠に非常に適しています。ただし、システムはこのような短い時間の一貫した遅延を維持できないため、特定の時点で実際の遅延が一定になることはないことに注意してください。

于 2015-07-14T15:57:51.650 に答える
10

はい、OS のタイム クォンタムを理解する必要があります。Windows では、タイム クォンタムを 1 ミリ秒に変更しない限り、1 ミリ秒の解決時間を得ることさえできません。(たとえば、timeBeginPeriod()/timeEndPeriod() を使用) それでも、実際には何も保証されません。少しの負荷または 1 つの安っぽいデバイス ドライバーでさえ、すべてを台無しにしてしまいます。

SetThreadPriority() は役立ちますが、非常に危険です。悪いデバイス ドライバーは、依然としてあなたを台無しにする可能性があります。

この醜いものを完全に機能させるには、非常に制御されたコンピューティング環境が必要です。

于 2008-09-17T16:49:19.743 に答える
7

通常、スリープは少なくとも次のシステム割り込みが発生するまで続きます。ただし、これはマルチメディアタイマーリソースの設定に依存します。1 ミリ秒に近い値に設定されている場合があります。一部のハードウェアでは、0.9765625 の割り込み周期で実行することもできます (によって提供されるActualResolutionは 0.9766 を表示しますが、実際には間違っています。ActualResolution形式NtQueryTimerResolutionに正しい数値を入れることはできません。それは 0.9765625 です。 1 秒あたり 1024 割り込みでミリ秒)。

割り込み期間よりも短い時間はスリープできない可能性があるという事実から逃れることができる例外が 1 つあります。それは有名なSleep(0). これは非常に強力なツールであり、必要なほど頻繁には使用されません。スレッドのタイム スライスのリマインダーを放棄します。このようにして、スケジューラがスレッドに強制的に CPU サービスを再度取得させるまで、スレッドは停止します。Sleep(0)は非同期サービスであり、この呼び出しにより、スケジューラは割り込みとは無関係に強制的に反応します。

2 番目の方法は、を使用することwaitable objectです。のような待機関数WaitForSingleObject()は、イベントを待機できます。スレッドをいつでも、またマイクロ秒単位でスリープ状態にするには、スレッドは、必要な遅延でイベントを生成するサービス スレッドをセットアップする必要があります。「スリープ」スレッドは、このスレッドをセットアップし、サービス スレッドがイベントをシグナル状態に設定するまで待機関数で一時停止します。

このようにして、どのスレッドも「スリープ」またはいつでも待機できます。サービス スレッドは非常に複雑になる可能性があり、マイクロ秒の分解能で時限イベントなどのシステム全体のサービスを提供する場合があります。ただし、マイクロ秒の解像度では、最大で 1 つの割り込み期間 (~1ms) の間、サービス スレッドが高解像度のタイム サービスでスピンするように強制される場合があります。注意を払えば、これは特にマルチプロセッサまたはマルチコア システムで非常にうまく動作する可能性があります。呼び出し元のスレッドとサービス スレッドのアフィニティ マスクが慎重に処理されている場合、1 ミリ秒のスピンはマルチコア システムで大きな影響を与えることはありません。

コード、説明、およびテストは、Windows タイムスタンプ プロジェクトで参照できます。

于 2012-07-12T16:12:46.743 に答える
6

何人かが指摘しているように、スリープおよびその他の関連機能はデフォルトで「システムティック」に依存しています。これは、OS タスク間の時間の最小単位です。たとえば、スケジューラはこれより速く実行されません。リアルタイム OS でも、システム ティックは通常 1 ミリ秒未満ではありません。これは調整可能ですが、スリープ機能だけでなく、システム全体に影響を与えます。これは、スケジューラがより頻繁に実行され、OS のオーバーヘッド (スケジューラの実行時間と実行時間の量) が増加する可能性があるためです。タスクを実行できる時間)。

これに対する解決策は、外部の高速クロック デバイスを使用することです。ほとんどの Unix システムでは、デフォルトのシステム クロックとは対照的に、使用するタイマーとそのような別のクロックを指定できます。

于 2008-09-17T18:03:10.953 に答える
5

非常に細分化したい場合は、間違った場所 (ユーザー空間) にいます。

ユーザー空間にいる場合、時間は必ずしも正確ではないことに注意してください。

スケジューラーはスレッド (またはアプリ) を開始してスケジュールできるため、OS スケジューラーに依存しています。

正確なものを探している場合は、次の手順を実行する必要があります。1) カーネル空間 (ドライバーなど) 2) RTOS を選択します。

とにかく、ある程度の粒度を探している場合 (ただし、ユーザー空間の問題を覚えておいてください)、MSDN の QueryPerformanceCounter 関数と QueryPerformanceFrequency 関数を参照してください。

于 2008-09-17T18:03:35.290 に答える
5

そのような精度を必要とするものを何を待っていますか? 一般に、そのレベルの精度を指定する必要がある場合(たとえば、一部の外部ハードウェアに依存するため)、間違ったプラットフォームを使用しているため、リアルタイム OS を確認する必要があります。

それ以外の場合は、同期できるイベントがあるかどうかを検討する必要があります。最悪の場合は、ビジー状態で CPU を待機し、高性能カウンター API を使用して経過時間を測定します。

于 2008-09-17T16:47:49.450 に答える
3

実際にこの usleep 関数を使用すると、大きなメモリ/リソース リークが発生します。(電話の頻度にもよりますが)

この修正版を使用してください (申し訳ありませんが編集できませんか?)

bool usleep(unsigned long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec / 1000000ul;
    tv.tv_usec = usec % 1000000ul;
    bool success = (0 == select(0, 0, 0, &dummy, &tv));
    closesocket(s);
    return success;
}
于 2009-09-19T16:34:33.450 に答える
2

SetWaitableTimerを使用してみてください...

于 2015-07-03T06:49:20.647 に答える
2

私は同じ問題を抱えており、スリープ(0)でさえ、ミリ秒より速いものはないようです。私の問題は、クライアントとサーバー アプリケーション間の通信で、_InterlockedExchange 関数を使用してテストとビットの設定を行った後、Sleep(0) を実行します。

この方法では、1 秒あたり何千もの操作を実行する必要があり、計画したほど速くは機能しません。

ユーザーを処理するシン クライアントがあり、エージェントがスレッドと対話するエージェントを呼び出すので、イベント インターフェイスが不要になるように、すぐにスレッドをエージェントとマージします。

この Sleep がどれほど遅いかを理解していただくために、空のループを実行して 10 秒間テストを実行しました (18,000,000 ループのようなものを取得)。つまり、100 倍遅くなります。

于 2009-05-13T20:02:20.400 に答える
1

誰もが言ったように、実際に睡眠時間についての保証はありません. しかし、アイドル状態のシステムでは、usleep コマンドが非常に正確な場合があることを誰も認めたくありません。特にティックレスカーネルでは。Windows Vista にはそれがあり、Linux には 2.6.16 以降があります。

Tickless カーネルは、ラップトップのバッテリ寿命を改善するために存在します: Intel の powertop ユーティリティを参照してください。

その状態で、たまたま Linux の usleep コマンドを測定したところ、要求されたスリープ時間が 6 マイクロ秒まで非常に厳密に守られていました。

したがって、おそらく OP は、アイドリング システムでほとんどの時間大まかに動作し、マイクロ秒のスケジューリングを要求できるものを望んでいます! 私は実際にWindowsでもそれを望んでいます。

また、Sleep(0) は boost::thread::yield() のように聞こえますが、この用語の方が明確です。

Boostのタイミングロックの方が精度が高いのではないかと思います。その後、誰も解放していないミューテックスをロックすることができ、タイムアウトに達しても続行できます...タイムアウトはboost::system_time + boost::milliseconds & cieで設定されます(xtimeは非推奨です)。

于 2012-05-11T15:50:02.383 に答える
0

たぶんミリ秒未満のスリープ機能

sleep(0) がうまくいったことがわかりました。タスク マネージャーで CPU の負荷がほぼ 0% のシステムで、単純なコンソール プログラムを作成し、sleep(0) 関数が一貫して 1 ~ 3 マイクロ秒 (1 ミリ秒未満) スリープ状態になるようにしました。

しかし、このスレッドの上記の回答から、CPU 負荷が大きいシステムでは、sleep(0) スリープの量がこれよりも大幅に変動する可能性があることがわかります。

しかし、私が理解しているように、スリープ機能をタイマーとして使用するべきではありません。これは、プログラムが使用する CPU のパーセンテージをできるだけ少なくし、できるだけ頻繁に実行するために使用する必要があります。ビデオゲームの画面上で発射物を 1 ミリ秒あたり 1 ピクセルよりもはるかに速く移動させるなど、私の目的では、sleep(0) が機能すると思います。

スリープ間隔が、スリープする最大時間よりもはるかに短いことを確認するだけです。スリープをタイマーとして使用するのではなく、ゲームが可能な限り最小限の CPU パーセンテージを使用するようにするだけです。特定の時間が経過したことを知るためにスリープすることとは関係のない別の関数を使用し、発射体を画面上で 1 ピクセル移動します。たとえば、1/10 ミリ秒または 100 マイクロ秒の時間.

擬似コードは次のようになります。

while (timer1 < 100 microseconds) {
sleep(0);
}

if (timer2 >=100 microseconds) {
move projectile one pixel
}

//Rest of code in iteration here

高度な問題やプログラムでは答えがうまくいかないかもしれませんが、一部または多くのプログラムではうまくいくかもしれません。

于 2015-10-31T05:33:02.700 に答える
0

Sleep(0) を使用するだけです。0 は明らかに 1 ミリ秒未満です。おかしく聞こえるかもしれませんが、私は真剣です。Sleep(0) は、今は何もする必要がないことを Windows に伝えますが、スケジューラが再び実行されたらすぐに再検討する必要があることを示します。明らかに、スケジューラ自体が実行される前にスレッドを実行するようにスケジュールすることはできないため、これは可能な限り最短の遅延です。

usleep にはマイクロ秒の数値を渡すことができますが、void usleep(__int64 t) { Sleep(t/1000); } - 実際にその期間をスリープ状態にする保証はありません。

于 2008-09-18T08:59:28.600 に答える
0

boost::xtime と timed_wait() を試してください

ナノ秒の精度を持っています。

于 2008-09-17T17:00:44.410 に答える
-4

Windows では、 を使用すると、アプリケーションで次のように初期化する必要があるWinsockselectライブラリを含める必要があります。

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);

そして、select はソケットなしで呼び出されることを許可しないため、microsleep メソッドを作成するためにもう少し行う必要があります。

int usleep(long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, &dummy, &tv);
}

これらのすべての作成された usleep メソッドは、成功するとゼロを返し、エラーの場合はゼロ以外を返します。

于 2008-09-17T16:39:51.980 に答える