1. 개요
프로그램에서 한 순간에 하나의 명령만을 실행하는(단일 PC값) 고전적인 관점에서 벗어나 멀티 쓰레드 프로그램
은 하나 이상의 실행 지점
(독립적으로 불러 들여지고 실행될 수 있는 여러 개의 PC값)을 가지고 있다.
멀티 쓰레드를 이해하는 방법은 각 쓰레드가 프로세스와 매우 유사하지만, 차이가 있다면 쓰레드들은 주소 공간
을 공유
하기 때문에 동일한 값에 접근할 수 있다는 것이다.
하나의 쓰레드의 상태는 프로세스의 상태와 매우 유사하다.
쓰레드는 어디서 명령어들을 불러 들일지 추적하는 프로그램 카운터(PC)
와 연산을 위한 레지스터
들을 가지고 있다.
만약 두 개의 쓰레드가 하나의 프로세서에서 실행 중이라면 실행하고자 하는 쓰레드(T2)는 반드시 문맥 교환(context switch)
을 통해서 실행 중인 쓰레드(T1)와 교체되어야 한다.
쓰레드 간의 문맥 교환은 T1이 사용하던 레지스터들을 저장하고 T2가 사용하던 레지스터의 내용으로 복원한다는 점에서 프로세스의 문맥 교환과 유사하다.
프로세스가 문맥 교환을 할 때에 프로세스의 상태를 프로세스 제어 블럭(PCB)에 저장하듯이 프로세스의 쓰레드들의 상태를 저장하기 위해서는 하나 또는 그 이상의 쓰레드 제어 블럭(TCB)
이 필요하다.
가장 큰 차이 중 하나는 프로세스의 경우와 달리 쓰레드 간의 문맥 교환에서는 주소 공간을 그대로 사용한다는 것이다.
쓰레드와 프로세스의 또 다른 차이는 스택
에 있다.
고전적 프로세스 주소 공간과 같은 간단한 모델(단일 쓰레드 프로세스)에서는 스택이 하나만 존재한다.
주로 주소 공간의 하부에 위치한다.
반면에 멀티 쓰레드 프로세스의 경우에는 각 쓰레드가 독립적으로 실행되며 쓰레드가 실행하기 위해 여러 루틴들을 호출할 수 있다.
주소 공간에는 하나의 스택이 아니라 쓰레드마다
스택이 할당되어 있다.
이를 쓰레드-로컬 저장소(thread-local storage)
라 부른다.
2. 왜 쓰레드를 사용하는가?
병렬 처리
느린 I/O로 인해 프로그램 실행이 멈추지 않도록 하기 위함
3. 예제: 쓰레드 생성
다음에 실행될 스레드는 OS 스케줄러(scheduler)
에 의해 결정되며, 스케줄러가 현명한 알고리즘을 구현할 법 하지만 특정 순간에 어떤 쓰레드가 실행될 지 알아내는 것은 어렵다.
4. 문제의 핵심: 제어 없는 스케줄링
경쟁 조건(race condition): 명령어의 실행 순서
에 따라 결과가 달라지는 상황, 데이터 경쟁(data race)라고도 불림
비결정적(indeterminate)인 결과: 컴퓨터의 작동에서 일반적으로 발생하는 결정적 결과
와 달리 결과가 어떠할지 알지 못하거나
실행할 때마다 결과가 다른
경우
임계 영역(critical section): 공유 변수(또는 공유 자원)
를 접근하고 하나 이상의 쓰레드에서 동시에 실행되면 안 되는
코드
이와 같은 문제들을 회피하려면 쓰레드는 상호 배제(mutual exclusion)
라는 기법을 사용하여서 하나의 쓰레드
만이 임계 영역에 진입할 수 있도록 보장한다.
그 결과로 경쟁을 피할 수 있고 프로그램 실행 결과를 결정적
으로 얻을 수 있게 된다.
5. 원자성
우리가 해야할 일은 하드웨어에 동기화 함수(synchronization primitives)
구현에 필요한 몇 가지 유용한 명령어를 요청하는 것이다.