CPU는 OS에 따라 Process 단위 또는 Thread 단위로 프로그램을 처리한다. (Linus, Unix의 경우 Process 단위, Window의 경우 Thread 단위)
Process/Thread는 상태(state)를 가지고 있는데, OS가 Process/Thread를 효율적으로 관리하기 위해 존재한다.
상태는 위 사진에 나타나지 않은 생성 상태와 완료를 포함하여 종 6가지 상태가 존재한다.
생성 상태부터 완료 상태까지를 활성 상태라고 하며, 일반적으로 아래와 같은 순서로 상태가 바뀌며 실행된다.
생성 상태 → 준비 상태 → 실행 상태 → 대기 상태 → 준비 상태 → 실행 상태 → 완료 상태
Process/Thread를 잠시 멈추기 위해 종종 sleep()
함수를 사용하게 된다.
in C
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
// in C++
#include <windows.h>
void Sleep(DWORD dwMilliseconds);
sleep()
함수는 초(second) 또는 밀리초(millisecond) 만큼 Process/Thread를 멈추는데, 해당 함수를 호출하면 OS는 해당 Process/Thread를 '보류 상태'로 전환하고 Process/Thread를 관리하는 목록에서 잠깐 제외시킨다.
즉, CPU를 선점하기 위한 목록에서 빠져나오게 된다.
이러한 상황은 중대한 사항을 발생시키는데, 만약 Process/Thread가 sleep(1)
을 호출하게 된다면 해당 Process/Thread가 1 초동안 쉬는 것이 아닌 1 초동안 Thread 스케쥴러에서 제외되는 것이다.
이후 해당 Process/Thread는 '준비 상태'로 자동으로 전환되면서 관리 목록에 다시 들어가게 되고, CPU 점유를 위해 추가적으로 대기해야하는 상황이 발생한다.
즉, 실제 원하는 시간보다 더 많은 시간을 소요하여 다시 실행된다는 것이다.
그래서 'sleep()' 함수가 지연시키는 시간은 OS 상태, CPU 상태에 따라 부정확하고 알 수 없으며, 이는 위험성을 야기한다.
아래는 CPU의 타이머 주파수를 이용해 Sleep() 함수 호출 후 실제로 흐른 시간을 출력하는 코드이다.
#include <iostream>
#include <windows.h>
int main()
{
LARGE_INTEGER freq;
LARGE_INTEGER begin;
LARGE_INTEGER end;
__int64 elapsed;
double during;
// CPU 타이머 주파수 확인
::QueryPerformanceFrequency(&freq);
std::cout << "초당 주파수: " << freq.QuadPart << std::endl;
for (int i = 0; i < 10; i++)
{
// 시작할 때 클럭(Clock) 수 저장
::QueryPerformanceCounter(&begin);
//////////////////////////////////////////////////
// 1 ms 중단
::Sleep(1);
/////////////////////////////////////////////////
// 끝났을 때 클럭 수 저장
::QueryPerformanceCounter(&end);
elapsed = end.QuadPart - begin.QuadPart;
during = (double) elapsed / freq.QuadPart;
std::cout << "실제로 흘러간 시간(micro): " << during * 100 * 1000 << std::endl;
}
return 0;
}
초당 주파수: 10000000
실제로 흘러간 시간(micro): 1279.41
실제로 흘러간 시간(micro): 1401.82
실제로 흘러간 시간(micro): 1375.61
실제로 흘러간 시간(micro): 1366.59
실제로 흘러간 시간(micro): 1423.19
실제로 흘러간 시간(micro): 1372
실제로 흘러간 시간(micro): 1485.09
실제로 흘러간 시간(micro): 1353.28
실제로 흘러간 시간(micro): 1384.54
실제로 흘러간 시간(micro): 1400.88