스레드 풀

스레드 풀은 애플리케이션을 대신하여 비동기 콜백을 효율적으로 실행하는 작업자 스레드의 컬렉션입니다. 스레드 풀은 주로 애플리케이션 스레드 수를 줄이고 작업자 스레드의 관리를 제공하는 데 사용됩니다. 애플리케이션은 작업 항목을 큐에 대기하고, 대기 가능한 핸들과 연결하고, 타이머에 따라 자동으로 큐에 대기하고, I/O로 바인딩할 수 있습니다.

스레드 풀 아키텍처

다음 애플리케이션은 스레드 풀을 사용하면 이점을 얻을 수 있습니다.

  • 매우 병렬이며 많은 수의 작은 작업 항목을 비동기적으로 디스패치할 수 있는 애플리케이션입니다(예: 분산 인덱스 검색 또는 네트워크 I/O).
  • 각각 짧은 시간 동안 실행되는 많은 수의 스레드를 만들고 삭제하는 애플리케이션입니다. 스레드 풀을 사용하면 스레드 관리의 복잡성과 스레드 만들기 및 소멸과 관련된 오버헤드를 줄일 수 있습니다.
  • 백그라운드 및 병렬로 독립적인 작업 항목을 처리하는 애플리케이션(예: 여러 탭 로드).
  • 커널 개체에서 배타적 대기를 수행하거나 개체의 들어오는 이벤트를 차단해야 하는 애플리케이션입니다. 스레드 풀을 사용하면 컨텍스트 스위치 수를 줄여 스레드 관리의 복잡성을 줄이고 성능을 높일 수 있습니다.
  • 이벤트를 대기할 사용자 지정 웨이터 스레드를 만드는 애플리케이션입니다.

원래 스레드 풀은 Windows Vista에서 완전히 다시 구조화되었습니다. 새 스레드 풀은 단일 작업자 스레드 유형(I/O 및 비 I/O 모두 지원)을 제공하고, 타이머 스레드를 사용하지 않고, 단일 타이머 큐를 제공하고, 전용 영구 스레드를 제공하기 때문에 개선되었습니다. 또한 클린 그룹, 더 높은 성능, 독립적으로 예약된 프로세스당 여러 풀 및 새 스레드 풀 API를 제공합니다.

스레드 풀 아키텍처는 다음으로 구성됩니다.

  • 콜백 함수를 실행하는 작업자 스레드
  • 여러 대기 핸들에서 대기하는 웨이터 스레드
  • 작업 큐
  • 각 프로세스에 대한 기본 스레드 풀
  • 작업자 스레드를 관리하는 작업자 팩터리

모범 사례

스레드 풀 API원래 스레드 풀 API보다 더 많은 유연성과 제어를 제공합니다. 그러나 몇 가지 미묘하지만 중요한 차이점이 있습니다. 원래 API에서 대기 재설정은 자동으로 설정되었습니다. 새 API에서 대기는 매번 명시적으로 다시 설정되어야 합니다. 원래 API는 호출 프로세스의 보안 컨텍스트를 스레드로 전송하여 가장을 자동으로 처리했습니다. 새 API를 사용하면 애플리케이션이 보안 컨텍스트를 명시적으로 설정해야 합니다.

다음은 스레드 풀을 사용하는 경우의 모범 사례입니다.

  • 프로세스의 스레드는 스레드 풀을 공유합니다. 단일 작업자 스레드는 한 번에 하나씩 여러 콜백 함수를 실행할 수 있습니다. 이러한 작업자 스레드는 스레드 풀에서 관리됩니다. 따라서 스레드에서 TerminateThread 를 호출하거나 콜백 함수에서 ExitThread 를 호출하여 스레드 풀에서 스레드를 종료하지 마세요.

  • I/O 요청은 스레드 풀의 모든 스레드에서 실행할 수 있습니다. 스레드 풀 스레드에서 I/O를 취소하려면 취소 함수가 I/O 요청을 처리하는 스레드와 다른 스레드에서 실행될 수 있으므로 동기화가 필요하며, 이로 인해 알 수 없는 작업이 취소될 수 있습니다. 이를 방지하려면 항상 비동기 I/O에 대해 CancelIoEx를 호출할 때 I/O 요청이 시작된 OVERLAPPED 구조를 제공하거나 사용자 고유의 동기화를 사용하여 CancelSynchronousIo 또는 CancelIoEx 함수를 호출하기 전에 대상 스레드에서 다른 I/O를 시작할 수 없도록 합니다.

  • 함수에서 반환하기 전에 콜백 함수에서 만든 모든 리소스를 정리합니다. 여기에는 TLS, 보안 컨텍스트, 스레드 우선 순위 및 COM 등록이 포함됩니다. 콜백 함수는 반환하기 전에 스레드 상태를 복원해야 합니다.

  • 스레드 풀이 핸들로 완료되었다는 신호를 표시할 때까지 대기 핸들과 연결된 개체를 활성 상태로 유지합니다.

  • 긴 작업(예: I/O 플러시 또는 리소스 정리)을 기다리는 모든 스레드를 표시하여 스레드 풀이 이 스레드를 기다리는 대신 새 스레드를 할당할 수 있도록 합니다.

  • 스레드 풀을 사용하는 DLL을 언로드하기 전에 모든 작업 항목, I/O, 대기 작업 및 타이머를 취소하고 콜백 실행이 완료되기를 기다립니다.

  • 콜백이 완료되기를 기다리지 않도록 하고 스레드 우선 순위를 유지하여 작업 항목과 콜백 간의 종속성을 제거하여 교착 상태를 방지합니다.

  • 기본 스레드 풀을 사용하여 다른 구성 요소와 프로세스에서 너무 많은 항목을 너무 빨리 큐에 대기하지 마세요. Svchost.exe 포함하여 프로세스당 하나의 기본 스레드 풀이 있습니다. 기본적으로 각 스레드 풀에는 최대 500개의 작업자 스레드가 있습니다. 스레드 풀은 준비/실행 상태의 작업자 스레드 수가 프로세서 수보다 작아야 하는 경우 더 많은 작업자 스레드를 만들려고 시도합니다.

  • COM 단일 스레드 아파트 모델은 스레드 풀과 호환되지 않으므로 사용하지 마십시오. STA는 스레드의 다음 작업 항목에 영향을 줄 수 있는 스레드 상태를 만듭니다. STA는 일반적으로 수명이 길고 스레드 선호도가 있으며 스레드 풀과 반대입니다.

  • 스레드 우선 순위 및 격리를 제어하고, 사용자 지정 특성을 만들고, 응답성을 향상시킬 수 있는 새 스레드 풀을 만듭니다. 그러나 추가 스레드 풀에는 더 많은 시스템 리소스(스레드, 커널 메모리)가 필요합니다. 풀이 너무 많을수록 CPU 경합 가능성이 높아질 수 있습니다.

  • 가능하면 APC 기반 메커니즘 대신 대기 가능한 개체를 사용하여 스레드 풀 스레드에 신호를 보냅니다. 시스템이 스레드 풀 스레드의 수명을 제어하므로 알림이 전달되기 전에 스레드가 종료될 수 있으므로 API는 스레드 풀 스레드를 다른 신호 메커니즘으로 사용할 수 없습니다.

  • 스레드 풀 디버거 확장인 !tp를 사용합니다. 이 명령에는 다음과 같은 사용법이 있습니다.

    • 주소플래그
    • obj 주소플래그
    • tqueue 주소플래그
    • 웨이터 주소
    • 작업자 주소

    풀, 웨이터 및 작업자의 경우 주소가 0이면 명령은 모든 개체를 덤프합니다. 웨이터 및 작업자의 경우 주소를 생략하면 현재 스레드가 덤프됩니다. 0x1(한 줄 출력), 0x2(덤프 멤버) 및 0x4(덤프 풀 작업 큐)의 플래그가 정의됩니다.

스레드 풀 API

스레드 풀 함수 사용