본문 바로가기
운영체제

스레드와 병행성

by SpearZero 2024. 10. 4.

스레드와 병행성

개요

  • 스레드는 CPU의 기본 단위이다.
  • 스레드는 스레드 ID, 프로그램 카운터(PC), 레지스터 집합, 스택으로 구성된다.
  • 스레드는 같은 프로스세스에 속한 다른 스레드와 코드 섹션, 데이터 섹션, 운영체제 자원을 공유한다.

동기

현대의 컴퓨터와 모바일 기기에서 실행되는 대부분의 소프트웨어 어플리케이션들은 멀티스레드를 이용한다. 어플리케이션은 일반적으로 여러 스레드의 제어와 함께 별도의 프로세스로 구현된다.

 

스레드의 예시

 

웹 브라우저는 하나의 스레드가 이미지 또는 텍스트를 표시하고 다른 스레드는 네트워크에서 데이터를 검색하도록 할 수 있다.

 

웹 서버가 수천명의 클라이언트로부터 자원에 대한 요청을 받을 수 있다. 만약 웹 서버가 단일 스레드 프로세스로 동작한다면 한 번에 하나의 클라이언트만 자원을 응답 받게된다.

 

이에 대한 해결책으로 서버에 요청이 올 때마다 별도의 프로세스를 생성해서 작업 후 응답을 하는 것이다. 하지만 프로세스 생성 작업은 많은 자원과 시간을 소비한다.

 

새 프로세스가 기존의 프로세스와 동일한 일을 한다면(자원 요청에 대한 응답) 프로세스를 계속 생성하는 것보다. 프로세스 안에 여러 스레드를 만드는것이 더 효율적이다.

 

서버는 클라이언트에서 요청이 오면 프로세스를 생성하는 대신 새 스레드를 생성한다. 그 후 추가적인 클라이언트 요청을 listen 하는 작업을 재개한다.

장점

다중 스레드 프로그래밍의 이점

  • 응답성(responsiveness) : 응용 프로그램의 일부분이 봉쇄되거나 응용 프로그램이 긴 작업을 수행하더라도 프로그램의 수행이 계속 되는것을 허용함으로써, 사용자에 대한 응답성을 증가시킨다.
  • 자원 공유(resource sharing) : 스레드는 자동으로 그들이 속한 프로세스의 자원들과 메모리를 공유한다. 한 응용 프로그램이 같은 주소 공간 내에 여러개의 다른 작업을 하는 스레드를 가질 수 있다.
  • 경제성(economy) : 일반적으로 스레드 생성은 프로세스 생성보다 시간과 메모리를 덜 소비하며, 문맥 교환이 더 빠르게 일어난다.
  • 규모 적응성(scalability) : 멀티 프로세서 구조에서 각각의 스레드가 다른 처리 코어에서 병렬로 실행 될 수 있다.

다중 코어 프로그래멍

컴퓨터 설계 역사의 초기에는 더 많은 컴퓨팅 성능에 대한 요구에 따라 단일 CPU 시스템이 다중 CPU 시스템으로 진화했다.

 

다중 코어

  • 단일 컴퓨팅 칩에 여러 컴퓨팅 코어를 배치하고, 각 코어가 운영체제에 대한 별도의 CPU로 나타난다. 이를 다중(멀티) 코어라고 한다. 즉, 두 개 이상의 독립 코어를 단일 집적 회로로 이루어진 하나의 패키지로 통합한 것이다.

다중 스레드 프로그래밍은 이러한 여러 컴퓨팅 코어를 보다 효율적으로 사용하고 병행성을 향상시키는 기법을 제공한다.

  • 병행성
    • 모든 작업이 진행되게 하여 둘 이상의 작업을 지원
  • 병렬성
    • 둘 이상의 작업을 동시에 수행

다중 프로세서 및 다중 코어 구조가 등장하기 이전에는 단일 프로세서만 있었으며 CPU 스케줄러는 프로세스 간에 빠르게 전환해 각 프로세스가 진행되도록 하여 병렬성의 환상을 제공하였다. 이는 병행하게 실행되었지만 변열로 실행되지는 않았다.

도전 과제

다중 코어 시스템을 프로그래밍 하기 위한 조건

  • 태스크 인식(identifying tasks) : 어플리케이션을 분석하여 독립된 병행 가능 태스크로 나눌 수 있는 영역을 찾는 작업이 필요하다.
  • 균형(balance) : 찾아진 태스크들이 균등한 기여도를 가지도록 나누는것도 중요하다.
  • 데이터 분리(data spliting) : 태스크가 접근하고 조작하는 데이터는 개별 코어에서 사용할 수 있도록 나누어 져야 한다.
  • 데이터 종속성(data dependency) : 태스크가 접근하는 데이터는 둘 이상의 태스크 사이에 종속성이 없는지 검토되어야 한다. 한 태스크가 다른 태스크로부터 오는 데이터에 종속적인 경우에는 프로그래머가 데이터 종속성을 수용할 수 있도록 태스크의 수행을 잘 동기화 해야 한다.
  • 시험 및 디버깅(testing and debugging) : 단일 스레드 어플리케이션을 시험하고 디버깅하는 것보다 훨씬 어렵다.

병렬 실행의 유형

  • 데이터 병렬 실행
    • 동일한 데이터의 부분집합을 다수의 계산 코어에 분배한 뒤 각 코어에서 동일한 연산을 실행하는 데 초점을 맞춘다.
  • 태스크 병렬 실행
    • 데이터가 아니라 태스크(스레드)를 다수의 코어에 분배한다. 각 스레드는 고유의 연산을 수행한다. 각 스레드들이 동일한 데이터에 대해 연산을 실행할 수 있고 또는 서로 다른 데이터에 연산을 실행할 수도 있다.

다중 스레드 모델

  • 사용자 스레드(user threads)
    • 커널 위에서 지원되며 커널의 지원 없이 관리된다.
  • 커널 스레드(kernel threads)
    • 운영체제에 의해 직접 지원되고 관리된다.

사용자 스레드와 커널 스레드는 연관관계가 존재한다. 사용자 스레드와 커널 스레드 사이에 다대일, 일대일, 다대다 모델이 존재한다.

다대일 모델

  • 많은 사용자 수준의 스레드를 하나의 커널 스레드로 매핑한다.
  • 스레드 관리는 사용자 공간(user space)의 스레드 라이브러리에 의해 행해진다.
  • 한 스레드가 봉쇄형 시스템 콜을 할 경우, 전체 프로세스가 봉쇄된다.
  • 한 번에 하나의 스레드만 커널에 접근할 수 있다.
    • 다중 코어 시스템에서 스레드는 병렬로 실행할 수 없음

일대일 모델

  • 각 사용자 스레를 하나의 커널 스레드로 매핑한다.
  • 하나의 스레드가 봉쇄형 시스템 콜을 호출하더라도 다른 스레드는 실행될 수 있다.
    • 다대일 모델보다 더 많은 병렬성을 제공한다.
  • 다중 처리기에서 다중 스레드가 병렬로 수행되는 것을 허용한다.
  • 단점으로는, 사용자 스레드를 생성하려면 대응하는 커널 스레드를 만들어야 한다. 많은 수의 커널 스레드는 시스템 성능에 부담을 줄 수 있다.

다대다 모델

  • 여러개의 사용자 스레드를 작거나, 혹은 같은 수의 커널 스레드에 매핑한다.
  • 다대일, 일대일 모델의 단점들을 어느정도 해결한다.
    • 필요한 만큼 많은 사용자 스레드를 생성할 수 있다. 그리고 상응하는 커널 스레드가 다중 처리기에서 병렬로 수행될 수 있다.
    • 스레드가 봉쇄형 시스템 콜을 발생시켰을 때, 커널이 다른 스레드의 수행을 스케줄 할 수 있다.
  • 구현하기가 어렵다.
    • 대부분의 시스템에서 처리 코어 수가 증가함에 따라 커널 스레드 수를 제한하는 것의 중요성이 줄어들었다. 결과적으로 대부분의 운영체제는 일대일 모델을 사용한다.

암묵적 스레딩

암묵적 스레드

  • 스레드의 생성 및 관리를 애플리케이션 개발자에서 컴파일러 및 런타임 라이브러리로 이전하는 것
    • 다중 코어 처리의 성장에 따라 많은 수의 스레드를 가진 어플리케이션이 등장하게 되었는데, 이러한 어플리케이션을 설계하는 것은 어렵다.
    • 이러한 어려움을 극복하고 병행 및 병렬 어플리케이션 설계를 더 잘 지원하기 위해 암묵적 스레드를 사용한다.

암묵적 스레드의 몇 가지 전략

스레드 풀

웹 서버에서 매 요청마다 스레드를 생성될 때 문제점

  • 매 요청마다 스레드를 생성할 때 마다 시간이 소요된다.
  • 스레드를 무한정 만들면 서버의 자원이 고갈된다.

이를 해결하기 위해 스레드 풀(pool)을 사용한다.

  • 프로세스를 시작할 때 일정한 수의 스레드 풀을 만들어둔다.
    • 요청이 발생하면 스레드 풀에서 사용 가능한 스레드를 가져와 요청을 처리한다.
    • 사용 가능한 스레드가 없으면 사용 가능한 스레드가 생길 떄 까지 대기한다.

스레드 풀의 장점

  • 스레드를 생성하는 것보다 기존 스레드로 서비스 해주는것이 빠르다.
  • 스레드의 개수를 제한할 수 있다. 많은 수의 스레드를 병렬 처리할 수 없는 시스템에 도움이 된다.
  • 수행할 태스크를 태스크 생성 메커니즘과 분리하면 태스크를 실행하기 위해 다양한 전략을 사용할 수 있다. 예를 들어, 태스크를 시간 지연 후에 실행되도록 예약하거나 주기적으로 실행하도록 할 수 있다.

Fork Join

메인 부모 스레드가 하나 이상의 자식 스레드를 생성(fork)한 다음 자식의 종료를 기다리고 join하고 그 시점부터 자식의 결과를 확인하고 결합할 수 있다. 암묵적 스레딩에서 fork 단계에서는 스레드가 직접 구축되지 않고 대신 병렬 작업이 식별된다.

 

라이브러리는 생성되는 스레드 수를 관리하며 스레드에 작업 배정을 책임진다. fork-join 모델은 라이브러리가 생성할 실제 스레드 수를 결정하는 동기 버전의 스레드 풀이다.

'운영체제' 카테고리의 다른 글

동기화 도구들  (1) 2024.11.09
CPU 스케줄링  (0) 2024.10.24
프로세스  (0) 2024.09.11
서론  (0) 2024.07.21