it-swarm-korea.com

Windows에서 스레드 절전 모드를 밀리 초 미만으로 만드는 방법

Windows에서는 Unix에서 발생하지 않은 문제가 있습니다. 이것이 1 밀리 초 미만 동안 스레드를 휴면 상태로 만드는 방법입니다. 유닉스에서는 일반적으로 필요에 따라 다양한 선택 (수면, 수면 및 나노 수면)이 있습니다. 그러나 Windows에서는 밀리 초 단위의 Sleep 만 있습니다.

유닉스에서는 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에서 어떻게 동일한 결과를 얻을 수 있습니까?

51
Jorge Ferreira

이것은 슬립 기능에 대한 오해를 나타냅니다. 전달하는 매개 변수는 최소 수면 시간입니다. 지정된 시간이 지나면 스레드가 깨어날 것이라는 보장은 없습니다. 실제로 스레드는 전혀 '깨어나지'않지만 스케줄러가 실행하도록 선택됩니다. 스케줄러는 스레드를 활성화하기 위해 요청 된 절전 시간보다 훨씬 더 오래 기다리도록 선택할 수 있습니다 (특히 그 순간에 다른 스레드가 여전히 활성 상태 인 경우).

89
Joel Coehoorn

Joel이 말했듯이, 그러한 짧은 기간 동안 의미있는 '수면'(예 : 예약 된 CPU 양도) 할 수 없습니다. 짧은 시간 동안 지연을 원한다면 적절한 고해상도 타이머 (예 : '성능 타이머')를 반복적으로 확인하고 우선 순위가 높은 것이 당신을 선점하지 않기를 바라고 회전해야합니다.

이러한 짧은 시간의 정확한 지연에 정말로 관심이 있다면 Windows를 사용하지 않아야합니다.

46
Will Dean

Winmm.lib에있는 고해상도 타이머를 사용하십시오. 예는 this 를 참조하십시오.

29
Joe Schneider
#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);
}

예, 문서화되지 않은 커널 함수를 사용하지만 잘 작동합니다. SleepShort (0.5); 내 threds의 일부에서

11
Oskar Dahlberg

예, OS의 시간 양자를 이해해야합니다. Windows에서는 시간 퀀텀을 1ms로 변경하지 않으면 1ms의 해결 시간을 얻지 못할 수도 있습니다. (예를 들어 timeBeginPeriod ()/timeEndPeriod () 사용) 그래도 실제로는 아무것도 보장하지 않습니다. 약간의 부하 또는 하나의 crappy 장치 드라이버조차도 모든 것을 버릴 것입니다.

SetThreadPriority ()가 도움이되지만 매우 위험합니다. 나쁜 장치 드라이버는 여전히 당신을 망칠 수 있습니다.

이 추악한 것들이 전혀 작동하도록하려면 매우 통제 된 컴퓨팅 환경이 필요합니다.

10
darron

너무 세분성을 원한다면 (사용자 공간에서) 잘못된 위치에 있습니다.

사용자 공간에 있다면 시간이 항상 정확하지는 않습니다.

스케줄러는 스레드 (또는 앱)를 시작하고 예약 할 수 있으므로 OS 스케줄러에 따라 다릅니다.

정확한 것을 찾고 있다면 가야합니다 : 1) 커널 공간 (드라이버와 같은) 2) RTOS를 선택하십시오.

어쨌든 약간의 세분성을 찾고 있다면 (사용자 공간의 문제를 기억하십시오) MSDN의 QueryPerformanceCounter Function 및 QueryPerformanceFrequency 함수를 찾으십시오.

6
user16523

일반적으로 슬립은 다음 시스템 인터럽트가 발생할 때까지 지속됩니다. 그러나 이것은 멀티미디어 타이머 리소스의 설정에 따라 다릅니다. 1ms에 가까운 것으로 설정 될 수 있으며, 일부 하드웨어는 NtQueryTimerResolution에서 제공 한 0.9765625 ( ActualResolution 의 인터럽트 기간 동안 실행되도록 허용합니다. 0.9766을 표시하지만 실제로는 잘못되었습니다. ActualResolution 형식으로 올바른 숫자를 입력 할 수 없습니다. 초당 1024 인터럽트에서 0.9765625ms입니다.

인터럽트 기간보다 잠을 잘 수 없다는 사실을 피할 수있는 예외가 있습니다. 유명한 Sleep(0)입니다. 이 도구는 매우 강력한 도구이므로 자주 사용하지 않습니다! 스레드의 시간 조각을 상기시켜줍니다. 이렇게하면 스케줄러가 스레드가 CPU 서비스를 다시 얻도록 강제 할 때까지 스레드가 중지됩니다. Sleep(0)은 비동기 서비스이므로 호출은 스케줄러가 인터럽트와 독립적으로 반응하도록합니다.

두 번째 방법은 waitable object를 사용하는 것입니다. WaitForSingleObject()과 같은 대기 함수는 이벤트를 기다릴 수 있습니다. 스레드가 마이크로 초 단위로 시간 동안 휴면 상태를 유지하기 위해서는 스레드가 원하는 지연으로 이벤트를 생성하는 서비스 스레드를 설정해야합니다. "sleeping"스레드는이 스레드를 설정 한 다음 서비스 스레드가 이벤트 신호를 설정할 때까지 대기 기능에서 일시 중지합니다.

이 방법으로 모든 스레드가 "잠자기"또는 언제든지 기다릴 수 있습니다. 서비스 스레드는 매우 복잡 할 수 있으며 마이크로 초 단위의 시간 초과 이벤트와 같은 시스템 전반의 서비스를 제공 할 수 있습니다. 그러나 마이크로 초 해상도는 서비스 스레드가 최대 1ms의 인터럽트 기간 동안 고해상도 시간 서비스에서 회전하도록 할 수 있습니다. 주의를 기울이면 특히 다중 프로세서 또는 다중 코어 시스템에서 매우 잘 실행될 수 있습니다. 호출 스레드 및 서비스 스레드에 대한 선호도 마스크를 신중하게 처리 할 경우 멀티 코어 시스템에서는 1ms 스핀이 크게 아프지 않습니다.

코드, 설명 및 테스트는 Windows Timestamp Project 에서 방문 할 수 있습니다.

5
Arno

여러 사람들이 지적했듯이 절전 및 기타 관련 기능은 기본적으로 "시스템 틱"에 따라 다릅니다. 이것은 OS 작업 사이의 최소 시간 단위입니다. 예를 들어 스케줄러는 이보다 더 빨리 실행되지 않습니다. 실시간 OS에서도 시스템 틱은 일반적으로 1ms 이상입니다. 스케줄러가 더 자주 실행되고 잠재적으로 OS의 오버 헤드 (스케줄러가 실행되는 시간 대 용량)가 증가하기 때문에 이는 조정 가능하지만 절전 기능뿐만 아니라 전체 시스템에 영향을 미칩니다. 작업을 실행할 수있는 시간).

이에 대한 해결책은 외부 고속 클록 장치를 사용하는 것입니다. 대부분의 유닉스 시스템에서는 기본 시스템 시계와 달리 타이머와 다른 시계를 지정할 수 있습니다.

5
mbyrne215

그러한 정확성을 요구하는 것은 무엇입니까? 일반적으로 need 해당 정밀도 수준을 지정하려면 (예 : 일부 외부 하드웨어에 대한 종속성으로 인해) 잘못된 플랫폼에 있고 실시간 OS를 확인해야합니다.

그렇지 않으면 동기화 할 수있는 이벤트가 있는지 아니면 최악의 경우 CPU를 기다렸다가 고성능 카운터 API를 사용하여 경과 시간을 측정해야하는지 고려해야합니다.

4
Rob Walker

SetWaitableTimer ...를 사용해보십시오.

2
andrewrk

실제로이 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;
}
2
Hendrik

나는 똑같은 문제가 있으며 심지어 Sleep보다 더 빠른 것 같지 않습니다. 내 문제는 클라이언트와 서버 응용 프로그램 간의 통신으로 _InterlockedExchange 함수를 사용하여 비트를 테스트하고 설정 한 다음 절전 (0)합니다.

나는이 방법으로 초당 수천 번의 작업을 수행해야하며 계획 한대로 작동하지 않습니다.

사용자를 다루는 씬 클라이언트가 있으며, 차례로 스레드와 통신하는 에이전트를 호출하므로 이벤트 인터페이스가 필요하지 않도록 스레드를 에이전트와 병합하기 위해 곧 이동할 것입니다.

이 수면이 얼마나 느린 지 아이디어를 제공하기 위해 빈 루프 (18,000,000 루프와 같은 것을 얻음)를 수행하는 동안 10 초 동안 테스트를 실행했지만 이벤트는 180,000 개의 루프 만 얻었습니다. 즉, 100 배 느립니다!

2
Celso Bressan

모든 사람들이 언급했듯이, 실제로 수면 시간에 대한 보장은 없습니다. 그러나 아무도 유휴 시스템에서 usleep 명령이 매우 정확하다는 것을 인정하기를 원하지 않습니다. 특히 틱리스 커널에서. Windows Vista에는 2.6.16부터 Linux가 있습니다.

노트북의 배터리 수명을 향상시키는 데 도움이되는 틱리스 커널 : c.f. 인텔의 파워 탑 유틸리티.

그 상태에서 나는 요청한 휴면 시간을 매우 십분의 마이크로 초까지 매우 밀접하게 존중하는 Linux usleep 명령을 측정했습니다.

따라서 OP는 유휴 시스템에서 거의 대부분 작동하고 마이크로 초 일정을 요청할 수있는 것을 원할 것입니다! 나는 실제로 Windows에서도 그것을 원할 것입니다.

또한 Sleep (0)은 boost :: thread :: yield ()처럼 들리는데,이 용어는 더 명확합니다.

Boost -timed lock의 정확도가 더 좋은지 궁금합니다. 그런 다음 아무도 릴리스하지 않은 뮤텍스를 잠글 수 있으며 시간 초과에 도달하면 계속 진행합니다.

1
Lightness1024

Boost :: xtime 및 timed_wait () 시도

나노초 정확도를가집니다.

0
theschmitzer

Sleep (0)을 사용하십시오. 0은 분명히 밀리 초보다 작습니다. 지금, 그것은 재미있게 들리지만 나는 진지합니다. Sleep (0)은 Windows에 현재 수행 할 작업이 없지만 스케줄러가 다시 실행 되 자마자 다시 생각하고 싶다고 말합니다. 분명히 스케줄러 자체가 실행되기 전에 스레드를 실행하도록 예약 할 수 없으므로 가능한 가장 짧은 지연 시간입니다.

마이크로 초 숫자를 usleep에 전달할 수 있지만 void usleep (__ int64 t) {Sleep (t/1000); }-해당 기간 동안 실제로 수면을 보장하지 않습니다.

0
MSalters

"pinwait를 수행하고 있기 때문에 "매우 짧은 시간 동안 기다립니다 " 목표 인 경우 수행 할 수있는 대기 수준이 증가합니다.

void SpinOnce(ref Int32 spin)
{
   /*
      SpinOnce is called each time we need to wait. 
      But the action it takes depends on how many times we've been spinning:

      1..12 spins: spin 2..4096 cycles
      12..32: call SwitchToThread (allow another thread ready to go on time core to execute)
      over 32 spins: Sleep(0) (give up the remainder of our timeslice to any other thread ready to run, also allows APC and I/O callbacks)
   */
   spin += 1;

   if (spin > 32)
      Sleep(0); //give up the remainder of our timeslice
   else if (spin > 12)
      SwitchTothread(); //allow another thread on our CPU to have the remainder of our timeslice
   else
   {
      int loops = (1 << spin); //1..12 ==> 2..4096
      while (loops > 0)
         loops -= 1;
   }
}

따라서 목표가 실제로 약간만을 기다리는 것이라면 다음과 같이 사용할 수 있습니다.

int spin = 0;
while (!TryAcquireLock()) 
{ 
   SpinOne(ref spin);
}

여기서 미덕은 매번 더 오래 기다렸다가 결국 완전히 자라는 것입니다.

0
Ian Boyd

밀리 초 미만의 절전 기능

Sleep (0)이 저에게 효과적이라는 것을 알았습니다. 작업 관리자의 CPU에 거의 0 %의 부하가 걸리는 시스템에서 간단한 콘솔 프로그램을 작성했으며 sleep (0) 함수는 1 ~ 3 마이크로 초로 잠을 잤습니다. 이는 밀리 초 미만입니다.

그러나이 스레드의 위의 답변에서 나는 CPU 부하가 큰 시스템에서 sleep (0) 수면의 양이 이것보다 훨씬 더 크게 변할 수 있음을 알고 있습니다.

그러나 내가 알기로는 수면 기능을 타이머로 사용해서는 안됩니다. 프로그램이 CPU의 최소 백분율을 사용하고 가능한 한 자주 실행하도록하는 데 사용해야합니다. 비디오 게임에서 화면을 가로 질러 투사 체를 밀리 초당 1 픽셀보다 훨씬 빠르게 움직이는 것과 같은 목적으로 sleep (0)이 작동한다고 생각합니다.

수면 간격이 최대 수면 시간보다 훨씬 작은 지 확인하십시오. 수면을 타이머로 사용하는 것이 아니라 게임에서 가능한 최소 CPU 비율을 사용하도록하기 위해서입니다. 별도의 기능을 사용하면 특정 시간이 지났을 때 알 수 있고 수면의 1/10 밀리 초 또는 100 마이크로 초의 시간에 화면을 가로 질러 투사 체를 한 픽셀 씩 움직일 수 있습니다. .

의사 코드는 다음과 같습니다.

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

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

//Rest of code in iteration here

답변이 고급 문제 나 프로그램에는 효과가 없지만 일부 또는 많은 프로그램에는 효과가있을 수 있음을 알고 있습니다.

0
rauprog