[운영체제(OS)] 스레드 (Thread), 멀티스레드(Multithreaded Programming)란?

두 개의 실행 스레드를 가진 프로세스가 하나의 프로세서 위에서 실행 중인 모습

1. Thread란?

CPU 수행의 기본단위이며 특히 프로세스 안의 흐름의 단위이다. 스레드가 수행되는 환경을 Task라고 하며 Thread ID, Program counter, register set, Stack space로 구성된다. 각각의 스레드는 레지스터 상태와 스택을 갖는다. Code, Data 섹션이나 운영체제 자원들은 스레드끼리 공유한다. 

스레드의 종류

스레드는 지원 주체에 따라 2가지로 나눌 수 있다.

User Threads

  • 유저 스레드는 사용자 수준의 스레드 라이브러리가 관리하는 스레드
  • 라이브러리는 스레드의 생성 및 스케쥴링 등 관리 기능을 제공한다.
  • 동일 메모리에서 스레드가 생성 및 관리되므로 속도가 빠르다.
  • 여러 개의 사용자 스레드 중 하나의 스레드가 시스템 호출 등으로 중단되면 나머지 스레드가 같이 종료된다. (커널이 프로세스 내부 스레드를 인식하지 못하여 해당 프로세스를 대기상태로 전환시키기 때문)
  • 스레드 라이브러리에는 POSIX, Pthreads, Win32 threads, Java threads 대표적이다

Kernel Threads

  • 커널 스레드는 커널이 지원하는 스레드
  • 커널 스레드를 사용하면 안정적이지만 유저모드에서 커널모드로 계속 바꿔줘야 하기에 성능이 저하된다.
  • 반대로 유저 스레드를 사용하면 안정성은 떨어지지만 성능이 저하되지는 않는다.
  • 스레드가 시스템 호출 등으로 중단되어도 다른 스레드를 중단시키지 않고 계속 실행시킨다.

Thread Group (스레드 그룹)

Thread Group (스레드 그룹)이란 관련 있는 스레드를 그룹으로 묶어 다루는 장치이다. 쓰레드 그룹은 다른 스레드그룹에 포함될 수 있으며, 트리형태로 연결된다. 스레드는는 자신이 포함된 스레드 그룹이나 하위 그룹에는 접근가능 하지만, 다른 그룹에는 접근할 수 없다.

Deamon Thread(데몬 스레드)

  • 다른 일반 스레드의 작업을 돕는 보조 쓰레드
  • 일반 스레드가 모두 종료되면 자동으로 종료
  • 일정시간마도 자동수행되는 저장/ 화면 갱신등에 사용

Thread Pools

스레드를 요청할 때마다 매번 새로 생성하고, 수행하고, 지우고 반복하면 성능저하로 이어진다.

그래서 미리 스레드 풀에 여러 개의 스레드를 만들어두고 요청이 오면 스레드풀에서 스레드를 할당해 주는 방법을 사용한다. 

2. 멀티스레드란?

한 번에 하나의 작업만 수행하면 싱글 스레드, 하나의 프로세스가 둘 이상의 스레드가 동시에 작업을 수행하면 멀티스레드라 한다.

멀티프로세싱 시스템이 여러 개의 완전한 처리 장치들을 포함하는 반면 멀티스레딩은 스레드 수준뿐 아니라 명령어 수준의 병렬 처리에까지 신경을 쓰면서 하나의 코어에 대한 이용성을 증가하는 것에 초점을 두고 있다. 

멀티스레드의 장점

두 프로세스가 하나의 데이터를 공유하려면 공유메모리 또는 파이프를 사용해야 하지만, 효율이 떨어지고 구현/관리하기 힘들다.

프로세스사이 콘텍스트 스위치가 지속적으로 일어나면 성능저하 발생 (스레드 전환 시에도 일어나지만 속도가 더 빠르다)

 

응답성 : 대화형 프로그램을 멀티스레드화 하면 일부 스레드가 중단되거나 긴 작업을 수행하더라도  다른 스레드가 별도의 작업을 할 수 있어 응답성이 좋다

자원공유 : 프로세스 내의 자원과 메모리를 공유함으로 시스템 자원의 낭비가 적다. 또한 같은 주소 공간 내에 여러 개의 활동성 스레드를 가질 수 있다는 장점이 있다.

경제성 : 메모리와 자원할당은 많인 비용이 소모된다. 스레드는 프로세스 내 자원을 공유하기에 스레드생성과 Context Switching을 하는 것이 효율적이다.

멀티프로세서 활용 : 각각의 스레드가 다른 프로세스에서 병렬로 수행 가능하다. 단일 쓰레드 프로세스는 CPU가 많아도 1개의 CPU에서만 실행되지만, 다중 스레드화 하면 다중 CPU에서 병렬성이 증간된다.

프로세스와 비교 

두 프로세스가 하나의 데이터를 공유하려면 공유메모리 또는 파이프를 사용해야 하지만, 효율이 떨어지고 구현/관리하기 힘들다.

스레드, 프로세스 사이 콘텍스트 스위치가 지속적으로 일어나면 성능저하 발생하나 스레드의 Context Switching의 속도가 더 빨라서 효율적이다.

멀티스레드의 단점

  • 캐시, 변환 생인 버퍼(TLB) 등의 하드웨어 리소스를 공유할 때 서로 간섭할 수 있다.
  • Context Switching 시간이 길수록 멀티 쓰레딩의 효율은 저하되어 단순 계산은 싱글 스레드 보다 실행시간이 개선되지 않고 오히려 지연될 수 있다.
  • 멀티 쓰레딩의 하드웨어 지원을 위해 애플리케이션, 운영체제 모두에 최적화 변경이 필요하다.
  • 각 스레드 중 어떤 것이 먼저 실행될지 그 순서를 알 수 없다.

예를 들어 스레드 1, 스레드 2로 다음 작업을 수행할 때,

- 공유되는 변수 i의 값을 레지스터에 저장
- 레지스터의 값을 1 증가
- 변수 i에 그 값을 저장
쓰레드 동작 i 스레드 1의 레지스터 스레드 2의 레지스터
스레드 1 i의 값을 레지스터에 저장 0 0  
스레드 1 레지스터 값을 1 증가 0 1  
스레드 1 i에 값 저장 1 1  
스레드 2 i의 값을 레지스터에 저장 1 1 1
스레드 2 레지스터 값을 1 증가 1 1 2
스레드 2 i에 값 저장 2 1 2

스레드 순서가 정상적으로 처리된다면 다음과 같이 최종적으로 i = 2가 되지만, 스레드 실행 순서가 달라진다면 

스레드 동작 i 스레드 1의 레지스터 스레드 2의 레지스터
스레드 1 i의 값을 레지스터에 저장 0 0  
스레드 2 i의 값을 레지스터에 저장 0 0 0
스레드 1 레지스터 값을 1 증가 0 1 0
스레드 2 레지스터 값을 1 증가 0 1 1
스레드 1 i에 값 저장 1 1 1
스레드 2 i에 값 저장 1 1 1

i = 1 이 되기에 의도와 다른 수행이 일어나며, 스레드의 실행조건에 따라 다른 결과를 나타내기에 원인 파악이 힘들다.

이러한 문제를 경쟁조건이라고 하며 세마포어 같은 방법으로 공유데이터에 접근하는 스레드의 개수를 한 개 이하로 유지하여 해결할 수 있다.

Context Switching

컴퓨터가 동시에 처리할 수 있는 작업 수는 CPU의 코어 수량과 같다. CPU 코어보다 많은 스레드가 동시에 실행되면 각 코어별로 정해진 시간만큼 번갈아가며 작업을 수행한다. 각 스레드가 서로 번갈아가며 교체될때 쓰레드간 현재까지의 작업상태나 다음 작업에 필요한 데이터를 저장하고 읽는 작업을 하는데 이를 Context Switching라고 한다. Context Switching 시간이 길수록 멀티 쓰레딩의 효율은 저하된다. 그래서 많은 양의 단순계산은 싱글 쓰레드로 처리하는 것이 효율적인 경우가 있기에 쓰레드 수가 많은 게 항상 고성능은 아니다.

Multithreaded Server Architecture

서버와 클라이언트 사이에도 멀티 스레드를 구현한다. 클라이언트가 새로운 요청을 하면 서버는 스레드를 새로 생성해서 요청을 수행한다.  프로세스 보다 쓰레드를 생성하는 것이 더 빠르기 때문에 효율적이다.

Multicore Programming

동시성(Concurrency)

동시성은 싱글 프로세스에서 사용되는 방식으로 프로세서가 여러 개의 스레드를 번갈아가면 수행하며 동시에 실행되는 것처럼 보이게 한다.

병렬성(Parallelism)

병렬성은 멀티코어 방식에서 사용되는 방식으로 여러 개의 코어가 스레드를 동시에 수행한다.

3. Multithreading Models

유저 스레드와 커널 쓰레드 관계를 정의하는 방식이다.

Many-to-One Model

  • 하나의 커널 스레드에 여러 개의 유저 스레드 연결
  • 사용자 수준에서의 스레드 관리
  • 주로 커널 스레드를 지원하지 않는 시스템에서 사용
  • 한 번에 하나의 유저스레드만 커널에 접근가능
  • 멀티코어 시스템에서 병렬적인 수행이 불가능

One-to-One Model

  • 하나의 유저 스레드에 하나의 커널 스레드가 대응하는 모델
  • Many-to-One방식에서 시스템 호출 시 다른 스레드들이 중단되는 문제를 해결할 수 있어 동시성 향상
  • 멀티프로세서 시스템에서는 동시에 여러 개 쓰레드 수행 가능
  • 유저 스레드 증가분만큼 커널 스레드가 증가.
  • 커널 스레드를 생성하는 것은 오버헤드가 큰 작업이기에 성능저하 발생가능

Many-to-Many Model

  • 여러 유저스레드가 더 적거나 같은 수의 커널 스레드에 대응하는 모델
  • 운영체제에 충분한 수의 커널 스레드를 생성가능
  • 커널 스레드의 구체적 개수는 프로그램이나 작동기기에 따라 상이
  • 멀티프로세서 프로그램에서는 싱글프로세서 보다 더 많은 커널 스레드가 생성
  • 커널이 사용자 스레드와 커널 스레드의 매핑을 적절하게 조절

Two-level Model

  • Many-to-Many 모델과 유사
  • 특정 유저 스레드를 위한 커널 스레드를 따로 제공하는 모델
  • 점유율이 높아야 하는 유저 스레드를 더 빠르게 처리 가능

 

참고

https://ko.wikipedia.org/wiki/스레드_(컴퓨팅) 

https://rebro.kr/174

http://www.tcpschool.com/java/java_thread_multi

https://ko.wikipedia.org/wiki/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%94%A9