공부/게임서버

Thread.Sleep 과 Thread.Yield

samosa 2024. 11. 12. 17:31
while (true)
        {
            if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0)
                return;
            
            // Option 1
            Thread.Sleep(1);
            
            // Option 2
            Thread.Sleep(0);
            
            // Option 3
            Thread.Yield();
        }

 

이 코드에서 Thread.Sleep(1), Thread.Sleep(0), 그리고 Thread.Yield()는 모두 CPU와 메모리 자원의 사용과 관련이 있다. 메모리 측면에서 각각의 동작을 더 깊이 설명하면 다음과 같다.

1. Thread.Sleep(1)

  • 동작: Thread.Sleep(1)은 현재 스레드를 최소 1밀리초 동안 대기 상태로 전환한다. 이때 운영 체제의 스케줄러는 다른 스레드에게 CPU 실행 권한을 넘겨준다.
  • 메모리 측면:
    • 스레드가 Sleep(1) 상태에 들어가면, 스레드의 실행 상태가 대기 상태로 변경되며 CPU에서 실행이 멈춘다. 이때 스레드는 현재 레지스터 값과 스택 포인터 등 컨텍스트 정보를 저장하고 메모리의 대기열에 포함된다.
    • CPU 캐시와 관련해, 스레드가 다시 활성화될 때 캐시가 다른 작업으로 인해 무효화될 수 있다. 이로 인해 스레드가 재개될 때 메모리의 캐시 미스(cache miss)가 발생할 가능성이 있다.
    • 이는 메모리 접근 시간과 CPU 효율성을 저하시킬 수 있다.

2. Thread.Sleep(0)

  • 동작: Thread.Sleep(0)은 현재 스레드의 실행을 즉시 양보하지만, 동일한 우선순위의 다른 스레드에게만 실행 기회를 제공한다. 만약 실행 대기 중인 같은 우선순위의 스레드가 없다면, 현재 스레드가 다시 실행된다.
  • 메모리 측면:
    • 스레드의 컨텍스트는 전환되지 않으므로 메모리 상태가 크게 변경되지 않는다. 그러나, CPU가 실행을 양보할 때 캐시 라인에 다른 스레드의 데이터가 로드될 수 있다.
    • 캐시 데이터가 그대로 유지되기 때문에, 다시 실행될 때 캐시 미스가 덜 발생할 수 있다. 이로 인해 메모리 액세스 시간과 CPU 효율성이 비교적 높다.

3. Thread.Yield()

  • 동작: Thread.Yield()는 스레드가 실행 중인 프로세서의 실행을 다른 논리적 프로세서나 다른 스레드에게 양보한다. 특히, 대기 중인 스레드가 있다면 실행을 넘기며, 없을 경우 현재 스레드가 계속 실행된다.
  • 메모리 측면:
    • Yield()는 컨텍스트 스위칭이 발생할 수 있으며, 이 과정에서 CPU의 캐시 상태가 바뀔 수 있다. 캐시 히트(cache hit) 비율은 여전히 유지될 수 있지만, 스레드가 완전히 대기 상태로 전환되는 Sleep(1)보다는 캐시 무효화의 가능성이 적다.
    • 메모리 일관성 측면에서 스레드가 다른 스레드에게 실행을 넘기면서 메모리 버퍼의 플러시(갱신)나 메모리 가시성 문제가 발생할 수 있다. 하지만 일반적으로 운영 체제의 메모리 관리 메커니즘에 의해 이러한 문제는 제어된다.

요약

  • Thread.Sleep(1)은 스레드를 대기 상태로 만들어 캐시가 무효화될 가능성이 높으며, 메모리 접근 성능이 떨어질 수 있다.
  • Thread.Sleep(0)은 컨텍스트 스위칭 없이 실행 우선순위를 조정하므로 메모리 캐시 상태가 유지되며, 메모리 액세스 성능이 비교적 높게 유지된다.
  • Thread.Yield()는 다른 스레드에게 CPU 실행을 넘기면서 캐시 무효화 가능성이 있지만, 캐시 히트율이 Sleep(1)보다는 높다.

이 기법들은 메모리와 CPU 사용률을 조절하기 위한 도구로, 시스템 성능 요구 사항에 따라 적절한 방법을 선택하여 사용한다.