4.1 개요
- 스레드는 CPU 이용의 기본 단위이다.
- 스레드는 같은 프로세스에 속한 다른 스레드와 코드, 데이터 섹션, 운영체제 자원들을 공유한다.
- 다중 스레드 프로세스는 동시에 하나 이상의 작업을 수행할 수 있다.
4.1.1 동기 (Motivation)
-
다중 스레드를 응용하면 다중 코어 시스템에서 처리 능력을 향상시키도록 설계될 수 있다.
-
하나의 응용 프로그램이 여러개의 작업을 수행할 필요가 있다.
단일 스레드로 작동한다면 한 번에 하나의 처리밖에 할 수 없기 때문에 사용자는 자신의 요구가 서비스되기까지 매우 긴 시간을 기다려야 한다.
-
새로운 프로세스를 생성하는 작업은 많은 시간과 자원을 소비한다. 하지만 새 프로세스와 기존 프로세스가 하는 일이 같다면 이러한 오버헤드를 감수할 이유가 없다.
-
프로세스 내부에 스레드를 생성하여 여러가지 작업을 수행하는 것이 더 효율적이다.
4.1.2 장점
1. 응답성
- 다중 스레드화하면 응용 프로그램이 긴 작업을 수행하더라도 프로그램의 수행이 계속되는 것을 허용한다.
- 사용자 인터페이스를 설계하는데 있어 특히 유용하다.
2. 자원 공유
- 프로세스는 IPC를 통해 자원을 공유하지만, 스레드는 자동으로 그들이 속한 프로세스의 자원과 메모리를 공유한다.
3. 경제성
- 프로세스를 생성하는 것보다 스레드를 생성하고 문맥을 교환하는 것이 시간과 메모리를 덜 소비한다.
4. 규모 적응성
- 다중 스레드의 이점은 다중 처리기 구조에서 더욱 증가할 수 있다.
4.2 다중 코어 프로그래밍
- 현대 컴퓨터는 다중 코어 형태이다.
- 다중 스레드 프로그래밍은 이러한 컴퓨팅 코어를 보다 효율적으로 사용하고 병행성을 향상시키는 기법을 제공한다.
- 단일 프로세서만 있던 컴퓨터는 프로세스간 빠른 전환을 통해 병렬성의 환상을 제공했다. 그러나 다중 스레드 프로그래밍과 다중 코어를 통해 프로세스를 병렬로 실행할 수 있다.
병행성

병렬성

4.2.1 프로그래밍 도전과제
- 운영체제 설계자는 병렬수행이 될 수 있도록 여러 코어를 활용하는 스케줄링 알고리즘을 개발해야 한다.
- 응용 프로그래머는 기존 프로그램을 다중 스레드를 사용하도록 수정해야 하고 새로운 다중 스레드 프로그램을 설계해야 한다.
1. 태스크 인식 (identifying tasks)
- 응용을 분석하여 독립된 병행 가능 태스크로 나눌 수 있는 영역을 찾는 작업이 필요하다.
- 태스크는 서로 독립적, 병렬 실행 가능해야 함.
2. 균형 (balance)
- 찾아진 부분들이 전체 작업에 균등한 기여도를 가지도록 태스크를 나누는 것이 중요하다.
- 기여도가 작게 분리된 태스크를 위해 별도 코어를 할당하는 것은 그만한 가치가 없다.
3. 데이터 분리 (data spliting)
- 태스크의 분리와 함께 태스크가 접근하고 조작하는 데이터 또한 개별 코어에서 사용할 수 있도록 나눠져야 한다.
4. 데이터 종속성 (data dependency)
- 태스크가 접근하는 데이터의 종속성을 확인하고, 종속적인 경우에는 데이터 종속성을 수용할 수 있도록 태스크의 수행을 잘 동기화해야 한다.
5. 시험 및 디버깅 (testing and debugging)
- 다중 코어에서의 정상적인 실행을 시험하고 디버깅하는 것은 단일 코어보다 근본적으로 어렵다.
4.2.2 병렬 실행의 유형
데이터 병렬 실행
- 동일 데이터의 부분집합을 다수의 계산 코어에 분배하고 각 코어에서 동일한 연산을 실행한다.

태스크 병렬 실행
- 데이터가 아닌 태스크(스레드)를 다수의 코어에 분배한다.
- 동일한 데이터에 대해 각 스레드가 연산을 수행한다.

데이터와 태스크 병렬 처리는 상호 배타적이지 않으며 두 전략을 혼합하여 사용할 수 있다.
4.3 다중 스레드 모델
사용자 스레드(user threads)와 커널 스레드(kernel threads)가 존재한다.
- 사용자 스레드는 커널 위에서 지원되며 커널의 지원 없이 관리된다.
- 커널 스레드는 운영체제에 의해 직접 지원되고 관리된다.

4.3.1 다대일 모델
- 하나의 커널 스레드가 다수의 사용자 스레드로 연결된다
- 한 번에 한 스레드만 커널에 접근할 수 있기 때문에 병렬 실행이 불가능하다.
- 다중 코어가 대부분인 지금은 사용되지 않는다.

4.3.2 일대일 모델
- 하나의 사용자 스레드가 봉쇄적 시스템 콜을 하더라도 다른 스레드가 실행될 수 있다.
- 사용자 스레드를 만들려면 해당 커널 스레드를 만들어야 하는데, 많은 커널 스레드가 시스템 성능에 부담을 줄 수 있다는 것이 유일한 단점이다.
- Linux, Windows

4.3.3 다대다 모델
- 일대일 모델과 일대다 모델의 단점들을 어느정도 극복한 모델이다.
-> 사용자 스레드를 많이 만들어도 되고, 병렬 실행도 가능하다.
- 구현하기 어렵다.

다대다 모델이 합리적으로 보이지만, 구현이 어렵다. 또 현재 대부분의 시스템에서 처리 코어 수가 늘어남에 따라 커널 스레드 수를 제한하는 것의 중요성이 줄어서 대부분 일대일 모델을 사용한다.
4.4 스레드 라이브러리
스레드 라이브러리를 구현하는 두 가지 방법
비동기 스레딩
- 부모가 자식 스레드와 독립적으로 실행된다.
- 서로 독립적이기 때문에 데이터 공유가 거의 없다.
- 다중 스레드 서버에서 사용되는 전략
동기 스레딩
- 부모가 자식 스레드의 종료를 기다렸다가 실행을 재개한다.
- 스레드 사이의 상당한 양의 데이터 공유를 수반한다.
4.5 암묵적 스레딩 (Implicit Threading)
4.5.1 스레드 풀
- 스레드는 프로세스에 비해 소요 비용이 적지만, 그럼에도 생성시 시간이 소요된다.
-> 작은 작업마다 스레드를 만들고 삭제하면 비효율적이다.
- 모든 요청마다 스레드를 만든다면 최대 스레드 생성 가능 한계를 정해야 한다.
-> 무작정 만들면 CPU, 메모리 등 자원 고갈
기본 아이디어
- 프로세스 시작 시 일정 수의 스레드를 미리 만들어둔다.
- 요청 시 스레드 풀에 있는 스레드를 이용해 처리한 후 반납한다.
- 스레드 풀에 이용 가능 스레드가 없으면 생길 때까지 대기한다.
장점
4.5.2 Fork Join
- 메인 부모 스레드가 자식 스레드를 생성(fork)한 후 자식의 종류를 기다린 후 join하고 그 시점부터 자식의 결과를 확인하고 결합할 수 있다.
- 라이브러리가 생성 스레드 수를 관리하고 스레드에 작업 배정을 책임진다.
4.5.3 OpenMP