스레드와 병행성
개요
스레드는 CPU 이용의 기본 단위이다. 스레드는 스레드 ID, 프로그램 카운터(PC), 레지스터 집합, 그리고 스택으로 구성된다. 스레드는 같은 프로세스에 속한 다른 스레드와 코드, 데이터 섹션, 그리고 열린 파일이나 신호와 같은 운영체제 자원들을 공유한다. 전통적인 프로세스는 하나의 제어 스레드를 가지고 있다. 만일 프로세스가 다수의 제어 스레드를 가진다면, 프로세스는 동시에 하나 이상의 작업을 수행할 수 있다.
장점
다중 스레드 프로그래밍의 이점은 다음과 같다.
- 응답성(responsiveness): 대화형 응용프로그램을 다중 스레드화하면 응용 프로그램의 일부분이 봉쇄되거나, 응용 프로그램이 긴 작업을 수행하더라도 프로글매의 수행이 계속되는 것을 허용함으로써, 사용자에 대한 응답성을 증가시킨다. 이 특징은 사용자 인터페이스를 설계하는 데 있어 매우 유용하다. 예를 들어 사용자가 시간이 많이 걸리는 연산을 시작했을 때 단일 스레드는 그 연산이 완료될 때까지 사용자에게 응답하지 않을 것이다. 대조적으로 시간이 오래 걸리는 연산이 별도의 비동기적 스레드에서 실행된다면 응용은 여전히 사용자에게 응답할 수 있다.
- 자원 공유(resource sharing): 프로세스는 공유 메모리와 메시지 전달 기법을 통하여 자원을 공유할 수 있다. 이러한 기법은 프로그래머에 의해 명시적으로 처리되어야 한다. 그러나 스레드는 자동으로 그들이 속한 프로세스의 자원들과 메모리를 공유한다. 코드와 데이터 공유의 이점은 한 응용 프로그램이 같은 주소 공간 내에 여러 개의 다른 작업을 하는 스레드를 가질 수 있다는 점이다.
- 경제성(economy): 프로세스를 생성을 위해 메모리와 자원을 할당하는 것은 비용이 많이 든다. 스레드는 자신이 속한 프로세스의 자원을 공유하기 때문에, 스레드를 생성하고 문맥교환하는 것이 더 경제적이다. 오버헤드의 차이를 경험적으로 측정하는 것은 어려울 수 있지만 일반적으로 스레드 생성은 프로세스 생성보다 시간과 메모리를 덜 소비한다. 또한 문맥 교환은 일반적으로 프로세스 사이보다 스레드 사이에서 더 빠르다.
- 규모 적응성(scalability): 다중 스레드의 이점은 다중 처리기 구조에서 더욱 증가할 수 있다. 다중 처리기 구조에서는 각각의 스레드가 다른 처리기에서 병렬로 수행될 수 있기 때문이다. 단일 스레드 프로세스는 처리기가 아무리 많더라도 오직 한 처리기 에서만 실행된다.
다중 코어 프로그래밍
병행 시스템은 모든 작업이 진행되게 하여 둘 이상의 작업을 지원한다. 이에 반해 병렬 시스템은 둘 이상의 작업을 동시에 수행할 수 있다. 따라서, 병렬성 없이 병행성을 가질 수 있다. 다주 처리기 및 다중 코어 아키텍처가 출현하기 전에 대부분의 컴퓨터 시스템에는 단일 프로세서만 있었으며 CPU 스케줄러는 프로세스 간에 빠르게 전환해 각 프로세스가 진행되도록 하여 병렬성의 환상을 제공하였다. 이러한 프로세스는 병행하게 실행되었지만 병렬로 실행되지는 않았다.
다중 스레드 모델
사용자 스레드를 위해서는 사용자 수준에서, 또는 커널 스레드를 위해서는 커널 수준에서 제공된다. 사용자 스레드는 커널 위에서 지원되며 커널의 지원 없이 관리된다. 반면에 커널 스레드는 운영체제에 의해 직접 지원되고 관리된다.
다대일 모델
다대일 모델은 많은 사용자 수준 스레드를 하나의 커널 스레드로 사상한다. 스레드 관리는 사용자 공간의 스레드 라이브러리에 의해 행해진다. 한번에 하나의 스레드만이 커널에 접근할 수 있기 때문에 다중 스레드가 다중 코어 시스템에서 병렬로 실행될 수 없다.
일대일 모델
일대일 몯레은 각 사용자 스레드를 각각 하나의 커널 스레드로 사상한다. 이 모델은 하나의 스레드가 봉쇄적인 시스템 콜을 호출하더라도 다른 스레드가 실행될 수 있기 때문에 다대일 모델보다 더 많은 병렬성을 제공한다. 단점은 사용자 스레드를 만들려면 해당 커널 스레드를 만들어야 하며 많은 수의 커널 스레드가 시스템 성능에 부담을 줄 수도 있다.
다대다 모델
다대다 모델은 여러 개의 사용자 수준 스레드를 그보다 작은 수, 혹은 같은 수의 커널 스레드로 멀티플렉스한다.
다대일 모델은 개발자가 원하는 만큼의 사용자 수준 스레드를 생성하도록 허용하지만, 커널은 한 번에 하나의 스레드만 스케줄 할 수 있기 때문에 진정한 병렬 실행을 획득할 수 없다. 일대일 모델은 더 많은 병행 실행을 제공하지만 개발자가 한 응용프로그램 내에 너무 많은 스레드를 생성하지 않도록 주의해야한다. 다대다 모델은 이러한 두 가지의 단점들을 어느 정도 해결했다. 개발자는 필요한 만큼 많은 수준 스레드를 생성할 수 있다. 그리고 상응하는 커널 스레드가 다중처리기에서 병렬로 수행될 수 있다. 또한, 스레드가 blocking 시스템 콜을 발생시켰을 때, 커널이 다른 스레 드의 수행을 스케줄할 수 있다.
다대다 모델의 변형은 여전히 많은 사용자 스레드를 적거나 같은 수의 커널 스레드로 멀티 플렉스 시키지만 또한 한 사용자 스레드가 하나의 커널 스레드에만 연관되는 것을 허용한다. 이 변형은 때로 two-level model이라고 불리며 다대다 모델이 논의된 모델 중 가장 융통성 있는 것처럼 보이지만 실제로는 구현하기가 어렵다. 또한 대부분의 시스템에서 처리 코어 수가 증가함에 따라 커널 스레드 수를 제한하는 것의 중요성이 줄어들었다.
결과적으로 대부분의 운영체제는 이제 일대일 모델을 사용한다.
스레드 라이브러리
스레드 라이브러리는 프로그래머에게 스레드를 생성하고 관리하기 위한 API를 제공한다.스레드 라이브러리를 구현하는데는 주된 두가지 방법이 있다.
- 커널의 지원 없이 완전히 사용자 공간에서만 라이브러리를 제공하는 것이다. 라이브러리의 모든 코드와 자료구조는 사용자 공간에 존재한다. 라이브러리의 함수를 호출하는 것은 시스템 콜이 아니라 사용자 공간의 지역 함수를 호출하게 된다는 것을 의미한다.
- 운영체제에 의해 지원되는 커널 수준 라이브러를 구현하는 것이다. 이 경우 라이브러리를 위한 코드와 자료구조는 커널 공간에 존재한다. 라이브러리 API를 호출하는 것은 커널 시스템 콜을 부르는 결과를 낳는다.
스레드의 생성의 일반적인 전략에는 비동기 스레딩과 동기 스레딩이 있다.
비동기 스레딩은 부모가 자식 스레드를 생성한 후 부모는 자신의 실행을 재개하여 부모와 자식 스레드가 서로 독립적으로 병행하게 실행된다. 스레드가 독립적이기 때문에 스레드 사이의 데이터 공유는 거의 없다. 비동기 스레딩은 다중 스레드 서버에서 사용되는 전략이고 반응형 사용자 인터페이스를 설계하는데에도 흔히 사용된다.
동기 스레딩은 부모 스레드가 하나 이상의 자식 스레드를 생성하고 자식 스레드 모두가 종료할 때까지 기다렸다가 자신의 실행을 재개하는 방식을 말한다. 여기서 부모가 생성한 스레드는 병행하게 실행되지만 부모는 자식들의 작업이 끝날 때까지 실행을 계속할 수 없다. 부모는 자식들이 조인한 후에야 실행을 재개할 수 있다. 통상 동기 스레딩은 스레드 사이의 상당한양의 데이터 공유를 수반한다. 예를 들어 부모 스레드는 자식들이 계산한 결과를 통합할 수 있다.
암묵적 스레딩(Implicit Threading)
다중 코어 처리의 지속적 성장에 따라 수백 또는 심지어 수천 개의 스레드를 가진 응용 프로그램이 등장하게 되었다. 이러한 스레드 관리의 어려움을 도와주는 한가지 방법은 스레딩의 생성과 관리 책임을 응용 프로그램 개발자로부터 컴파일러와 실행시간 라이브러리에게 넘겨주는 것이다. 암묵적 스레딩이라고 불리는 이 전략은 점점 더 널리 사용되고 있다.