멀티 스레드

일반적으로 하나의 프로세스는 하나의 스레드를 가지고 작업을 수행하게 된다. 멀티 스레드란 하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것을 의미한다.

image


멀티 스레딩의 장점

멀티 스레드는 각 스레드가 자신이 속한 프로세스의 메모리를 공유하므로, 시스템 자원의 낭비가 적다. 특히 스레드의 문맥 교환은 프로세스의 문맥 교환과 달리 캐시 메모리를 비울 필요가 없기 때문에 더 빠르다. 따라서 시스템의 처리율이 향상되고 자원 소모가 줄어들어 자연스럽게 프로그램의 응답 시간이 단축된다. 또한, 하나의 스레드가 작업을 할 때 다른 스레드가 별도의 작업을 할 수 있어 사용자와의 응답성도 좋아진다. 이러한 장점 때문에 여러 프로세스로 할 수 있는 작업들을 하나의 프로세스에서 스레드로 나눠 수행하는 것이다.


멀티 스레딩의 단점

멀티 스레딩을 기반으로 프로그래밍을 할 때는 주의해야 할 점이 있다. 여러 개의 스레드가 데이터와 힙 영역을 공유하기 때문에 동시에 접근을 시도 할 때 접근의 타이밍이나 순서 등이 결괏값에 영향을 줄 수 있다는 것이다. 그렇기 때문에 멀티 스레딩 환경에서는 동기화 작업이 필요하다.

동기화란?

시스템의 한정적인 자원에 여러 스레드가 동시에 접근해서 사용하려고 하면 문제가 발생할 수 있다. 이런 문제를 방지하기 위해 스레드들에게 하나의 자원에 대한 처리 권한을 주거나 순서를 조정해주는 기법이다.

하지만 이로 인해 병목현상이 발생하여 성능이 저하될 가능성이 높다.

병목현상(Bottleneck)

병목현상전체 시스템의 성능이나 용량이 하나의 구성요소로 인해 제한을 받는 현상을 말한다. 병의 몸통(전체 시스템)보다 입구(하나의 구성요소) 부분이 좁아서 물이 상대적으로 천천히 쏟아지는 것과 비슷한 현상이다.

공유 자원이 아닌 부분까지 과도하게 동기화를 하게 되면 현재 스레드는 락(lock)을 획득한 스레드가 종료하기 전까지 대기해야 한다. 그렇게 되면 전체 성능에 악영향을 미치게 된다. 따라서 동기화를 하고자 할 때는 메소드 전체를 동기화 할 것인지, 아니면 특정 부분만 동기화할 것인지를 고민해야 한다.

임계 영역(Critical Section)

두 개 이상의 프로세스가 동시에 사용할 수 없는 자원에 대해 접근하고 실행하는 프로그램 내의 코드 부분을 임계 영역이라고 한다.

멀티 스레딩에서는 스레드가 자원을 공유하고 있기 때문에 동일한 자료에 동시에 접근할 때 결과가 달라지는 임계 영역이 발생할 수 있다. 임계 영역의 성공적인 실행을 위해서는 가장 먼저 상호 배제가 지켜져야 하고, 상호 배제란 한 번에 하나의 프로세스만이 임계 영역에 들어가야 함을 의미한다.

임계 영역을 해결하기 위한 방법은 크게 세 가지가 있다.

  • 뮤텍스(Mutual Exclusion, Mutex)
    뮤텍스는 공유 자원을 사용하기 전에 설정하고 사용한 후에 해지하는 잠금이다. 사물함의 특정 칸을 자물쇠로 잠궈놓고 사용하면 본인 외에는 아무도 사용할 수 없는 것처럼, 뮤텍스로 잠금이 설정되면 다른 스레드는 잠긴 코드 영역에 접근할 수 없다. 뮤텍스는 잠금 or 잠금 해제 둘 중 하나의 상태만을 가지게 된다.

  • 세마포어(Semaphore)
    세마포어는 다익스트라가 처음 제안한 개념으로, 특수한 명령들만 접근할 수 있게 허용되는 보호된 변수이다. 현재 공유자원에 접근할 수 있는 스레드, 프로세스의 수를 나타내며, 뮤텍스와 비슷한 역할을 하지만 세마포어는 동시 접근 동기화보다는 접근 순서 동기화와 더 관련이 있다.

    예를 들어, 프린터가 10개 있는 실습실에서 비어있는 프린터가 있다면 누구나 출력 작업을 할 수 있도록 해보자. 그럼 아래와 같이 생각할 수 있다.

    • 프린터: 공유자원
    • 출력 작업: 임계 영역
    • 세마포어: 작업 가능한 프린터 대수(초기값: 10)

프린터가 빌 때마다 새로운 사람이 출력 작업을 시작할 수 있고, 이처럼 한 번에 많은 인원을 수용할 수 있다는 점이 뮤텍스와의 차이점이다.

세마포어에 접근 가능한 명령들은 크게 P 명령V 명령이 있다. P 명령은 wait 또는 down이라고 불리기도 하며, V 명령은 signal 또는 up이라는 이름으로 사용되기도 한다.

🤷‍♀️ 왜 다익스트라는 P와 V로 이름을 붙였을까?
다익스트라가 태어난 곳인 네덜란드에서는 깃발을 내리다(down)의 동사가 P로 시작하고, 올리다(up)의 동사는 V로 시작한다. 도로 공사 중인 현장에서 깃발을 사용해 차량 통행을 제어하는 경우를 세마포어에 적용한 것이다.

  • P 명령(wait, down): 자신의 차례가 올 때까지 대기 ("멈춰~")
  • V 명령(signal, up): 다음 프로세스로 순서를 넘겨줌 ("지나가~")

  P(S): if(S > 0) 
            then S = S - 1;
        else 
            S > 0  조건 만족될 때까지 큐에서 대기;

  V(S): if(큐에서 대기 중인 프로세스들이 존재)
            then 그 중 한 프로세스를 준비 or 실행 상태로 만듦;
        else
            S = S + 1; 


  • 모니터(Monitor)
    모니터는 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고 해당 접근에 대한 인터페이스만 제공하는 모듈이다. 시스템이 모니터라는 큰 틀을 제공해주면, 우리는 이 구조를 활용하여 이 안에 추가적인 코딩을 하여 임계 영역을 관리하는 것이다. 꽤 많은 부분을 시스템이 대신 해주기 때문에 사용자의 실수 위험을 줄일 수 있다.

    모니터는 모니터 큐를 통해 공유 자원에 대한 작업들을 순차적으로 처리한다. 또한 언제나 모니터의 진입을 하나의 프로세스로 제한한다. 즉, 한 번에 하나 이하의 프로세스만이 모니터 내에 있게 함으로써 상호 배제를 자연스럽게 실현할 수 있다. 세마포어에서는 상호 배제를 명시적으로 구현해야 한다는 것이 모니터와의 차이점이다.



뮤텍스 vs 모니터

구분 뮤텍스 모니터
사용 목적 동기화 대상 하나 동기화 대상 여러개
제공자 운영체제 커널 프레임워크 or 라이브러리
(Java는 기본적으로 모든 객체에 모니터 제공)
락(lock) O X
속도 느림 빠름


세마포어 vs 모니터

구분 세마포어 모니터
상호 배제 구현 사용자가 명시 자동 구현


뮤텍스 vs 세마포어

구분 뮤텍스 세마포어
동기화 목적 동시 접근 동기화 접근 순서 동기화
동기화 개수 1개 1개 이상
소유 가능(key) 불가능

접근 가능한 공유 자원의 개수가 1개 이하라면 이진 세마포어뮤텍스처럼 활용할 수 있다. 그래서 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없는 것이다.

셀프 계산대가 한 개 밖에 없는 무인마트에서는 현재 계산 중인 사람이 계산을 마쳐야 다음 사람이 계산을 할 수 있다. 이렇게 한 번에 하나의 작업만 처리하는 것이 뮤텍스이다. 하지만 셀프 계산대가 여러 대라면 동시에 여러 사람이 계산을 진행할 수 있고, 계산을 기다리던 사람은 비어있는 아무 계산대에 가서 계산을 하면 된다. 이처럼 동시에 여러 개의 작업을 처리하는 것이 세마포어이다.

계산대가 여러개인 마트에 뮤텍스를 적용한다는 것은 각 사람마다 맘에 드는 계산대를 하나씩 정해서 그 앞에서만 대기하는 것이다. 내 옆의 계산대가 비더라도 나는 현재 계산대를 선택했기 때문에 비어있는 곳으로 갈 수 없는 것이다. 굉장히 비효율적이다.

따라서 어떤 경우에 뮤텍스를 적용하고 세마포어를 적용할 것인지 적절하게 결정해야 한다.


한 눈에 비교하는 멀티 스레드 vs 멀티 프로세스

구분 멀티 스레드 멀티 프로세스
개념 하나의 프로세스에 여러 스레드로
자원을 공유하며 작업을 나누어 수행
두 개 이상의 프로세서(CPU)가
하나 이상의 작업을 동시에 처리(병렬처리)
동시성 O O
메모리 공간 적게 차지 많이 차지
문맥 전환 빠름 느림
독립성 특정 스레드 종료 시
전체 스레드 종료될 수 있음
각 프로세스는 독립적이기에 영향 없음



References