스레딩 이슈

다중 스레딩은 개발자의 생산성을 높이고 애플리케이션 성능을 향상시키지만, 몇 가지 복잡한 이슈를 야기합니다.

fork() 및 exec() 시스템 호출

  • fork() 시스템 호출은 새로운 프로세스를 생성할 때 부모 프로세스의 모든 스레드를 복제할지, 아니면 단일 스레드만 복제할지에 대한 질문을 제기합니다.

    • 대부분의 Unix 시스템은 exec() 호출 없이 fork()를 수행할 경우 모든 스레드를 복제합니다. 이는 fork() 직후 자식 프로세스가 exec()를 호출하여 새로운 프로그램을 로드하는 것이 일반적이기 때문입니다. 이 경우, 모든 스레드를 복제하는 것은 불필요한 오버헤드를 발생시킵니다.

    • 따라서 많은 운영체제는 fork() 호출 시 단일 스레드(호출 스레드만)만 복제하는 버전을 제공합니다. 예를 들어, Solaris의 fork()는 모든 스레드를 복제하고, fork1()은 호출 스레드만 복제합니다.

  • exec() 시스템 호출은 프로세스의 전체 주소 공간을 요청된 파일로 덮어쓰므로, exec()를 호출하면 새로운 프로그램을 로드하여 기존 스레드가 모두 사라집니다.

시그널 처리

시그널은 Unix 시스템에서 비동기적으로 발생한 이벤트를 프로세스에 알리는 데 사용됩니다. 다중 스레드 프로그램에서 시그널을 처리하는 것은 복잡합니다. 시그널은 다음 중 하나로 전달될 수 있습니다:

  • 시그널이 적용되는 특정 스레드.

  • 프로세스의 모든 스레드.

  • 프로세스 내의 특정 스레드 목록.

  • 프로세스의 기본(main) 스레드에만.

운영체제마다 시그널 처리 방식이 다릅니다. 예를 들어, Unix와 Linux는 특정 스레드를 대상으로 하는 시그널(동기 시그널)과 프로세스 전체에 적용되는 시그널(비동기 시그널)을 구분합니다. 비동기 시그널은 일반적으로 기본 스레드에 전달되거나, 시그널을 기다리도록 등록된 특정 스레드에 전달될 수 있습니다.

스레드 취소

스레드 취소는 스레드가 작업을 완료하기 전에 종료되는 것을 의미합니다. 취소 대상 스레드는 취소 시점에 따라 두 가지 방식으로 종료될 수 있습니다:

  • 비동기 취소 (Asynchronous cancellation): 대상 스레드를 즉시 종료합니다. 이는 스레드가 보유하고 있는 자원(예: 뮤텍스 잠금)을 해제하지 못하게 할 수 있어 데이터 일관성 문제를 야기할 수 있습니다.

  • 지연 취소 (Deferred cancellation): 대상 스레드가 주기적으로 취소 지점(cancellation point)을 확인하고, 취소 요청이 있을 경우 안전하게 종료합니다. 이 방식은 스레드가 자원을 해제할 시간을 주어 데이터 일관성을 유지하는 데 도움이 됩니다.

  • POSIX Pthreads API는 스레드 취소를 지원하며, 스레드를 취소 가능(cancellable)하게 만들거나 취소 불가능(uncancellable)하게 설정할 수 있습니다. 기본값은 지연 취소입니다.

스레드 로컬 저장소 (Thread-Local Storage, TLS)

다중 스레드 프로세스에서 스레드들은 코드, 데이터, 힙 섹션을 공유하지만, 스택과 레지스터는 각 스레드마다 고유합니다. 스레드 로컬 저장소(TLS)는 각 스레드가 자신만의 데이터 인스턴스를 유지해야 할 때 유용합니다. 이는 정적 데이터와 유사하지만, 정적 데이터가 프로세스 내의 모든 스레드에 의해 공유되는 반면, TLS의 데이터는 스레드별로 고유하게 유지됩니다.

스케줄러 액티베이션 (Scheduler Activations)

스레드 라이브러리와 커널 스레드 간의 통신 모델입니다. 사용자 수준 스레드와 커널 스레드 간의 효율적인 매핑을 관리합니다. 사용자 스레드가 차단될 때 커널이 스레드 라이브러리에 업콜(upcall)을 사용하여 이벤트를 알리면, 라이브러리는 스케줄러 액티베이션을 사용하여 다른 사용자 스레드를 실행합니다. 이는 사용자 수준 스레딩의 유연성과 커널 수준 스레딩의 동시성 이점을 결합하는 시도입니다.

Last updated