멀티 스레드 프로그래밍
- 멀티스레드 프로그래밍은 하나의 프로세스에서 여러 개의 스레드를 만들어 자원의 생성과 관리의 중복을 최소화하는 것이다.
- 장점
- 멀티 프로세스에 비해 메모리 자원소모가 줄어든다.
- Heap 영역을 통해서 스레드 간의 통신이 가능하기 때문에 프로세스 간의 통신이 간단해진다.
- 스레드의 컨텍스트 스위칭은 프로세스의 컨텍스트 스위칭보다 훨씬 빠르다.
- 단점
- 힙 영역에 있는 자원을 사용할 때 동기화를 해야한다.
- 동기화를 위해서 락을 과도하게 사용하면 성능 저하가 발생할 수도 있다.
- 하나의 스레드가 비정상적으로 동작하면 다른 스레드도 영향을 받아 종료하게 될 수도 있다.
스레드 안전(Thread-Safety)
- 스레드 안전(Thread-Satety)란 멀티 스레드 프로그래밍에서 일반적으로 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없는 것을 말한다.
- 하나의 함수가 한 스레드로부터 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하여 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 옳바르게 나오는 것을 말한다.
Thread-sate하다라는 의미는 두 개 이상의 스레드가 race condition에 들어가거나 같은 객체에 동시에 접근해도 연산 결과는 정합성이 보장될 수 있게 메모리 가시성이 확보된 상태이다.
스레드 안전(Thread-Safety)의 여부 판단 방법
- 다음과 같이 세 가지를 통해 thread-safe한 상태인지 판단할 수 있다.
1. 전역 변수나 힙, 파일과 같이 여러 스레드가 동시에 접근할 수 있는 자원을 사용하는가?
2. 핸들과 포인터를 통한 데이터의 간접 접근이 가능한가?
3. 부수 효과를 가져오는 코드가 있는가?
스레드 안전을 지키기 위한 4가지 방법
- Mutual Exclusion (상호 배제)
- Atomic Operation (원자 연산)
- Thread-Local Storage (쓰레드 지역 저장소)
- Re-Entrancy (재진입성)
1) Mutual Exclusion( 상호 배제 )
- 공유 자원에 하나의 Thread만 접근할 수 있도록 세마포어 / 뮤텍스로 락을 통제하는 방법이다.
- 일반적으로 많이 사용하는 방법이다.
- 적용 예시
- Python은 Thread Safe하게 메모리를 관리하지 않아 GIL(Global Interpreter Lock)을 통해 Thread Safe를 보장한다.
- Python에서 threading.lock이 있는데 threading.lock을 acqurie하면 해당 쓰레드만 공유 데이터에 접근할 수 있고 lock을 release해야만 다른 쓰레드에서 공유 데이터에 접근할 수 있다.
2) Atomic Operation (원자 연산)
- 공유 자원에 접근할 때는 원자 연산을 이용하거나 원자적으로 정의된 접근 방법을 사용함으로써 상호 배제를 구현할 수 있다.
- Atomic
- 공유 자원 변경에 필요한 연산을 원자적으로 분리한 뒤에 실제로 데이터의 변경이 이루어지는 시점에 Lock을 걸고, 데이터를 변경하는 시간 동안 다른 쓰레드의 접근이 불가능하도록 하는 방법이다.
- 적용 예시
- a += b의 경우 먼저 +연산을 한 뒤에 = 연산을 함으로, 원자적이라고 볼 수 없다.
3) Thread-Local Storage (스레드 지역 저장소)
- 공유 자원의 사용을 최대한 줄이고 각가의 쓰레드에서만 접근 가능한 저장소들을 사용함으로써 동시 접근을 막는 방법이다.
- 일반적으로 공유 상태를 피할 수 없을 때 사용하는 방식이며, 전역 변수 사용을 자제하라는 뜻으로 생각하면 된다.
4) Re-entrancy (재진입성)
- 쓰레드 호출과 상관 없이 프로그램에 문제가 없도록 작성하는 방법이다.
- 어떤 함수가 한 스레드에 의해 호출되어 실행 중이라면 다른 스레드가 그 함수를 호출하더라도 그 결과가 각각에게 옳바르게 주어져야 한다.
- 쓰레드끼리 독립적으로 동작할 수 있도록 코드를 작성하는 것으로 생각하면 된다.
JAVA 프로그래밍에서 Thread-Safe하게 설계하는 방법
- java.util.concurrent 패키지 하위의 클래스를 사용한다.
- 인스턴스 변수를 두지 않는다.
- Singleton 패턴을 사용한다.
- 동기화 (Syncronized) 블럭에서 연산을 수행한다.
** 이때, 일반적으로 구현하는 Singleton Pattern은 Thread-Safe하지 않는다.
1. 싱글톤 패턴
- 싱글톤 패턴이란 애플리케이션이 시작되고 하나의 클래스 인스턴스를 보장해서 전역적인 접근점을 제공하는 패턴을 말한다.
- 장점
- 전역 변수를 사용하지 않기 때문에 디버깅이 쉽고, 네임 스페이스가 망가지는 일이 없다.
- 유일하게 존재하는 인스턴스로의 접근으로 통제가 가능하며, 데이터 공유가 쉬워진다.
- 단점
- 싱글톤 인스턴스가 너무 많은 일을 하거나 너무 많은 데이터를 가질 경우 결합도가 높아진다.
- 멀티스레드 환경에서 동기화 처리를 하지 않으면 여러 개가 생성될 수 있다.
2. 멀티스레드 환경에서 안전한 싱글톤 인스턴스 만들기
- 게으른 초기화(Syncronized 블록 사용)
- 속도가 너무 느려서 권장 되지 않음
- Double-Check Locking
- if문으로 존재 여부 체크하고, Syncronized 블록 사용하는 방법
- 어느정도 문제 해결은 가능하지만 완벽하지 않음
- Holder에 의한 초기화
- 클래스 안에서 클래스(Holder)를 두어 JVM Class Loader 매커니즘을 이용한 방법
- 개발자가 직접 동기화 문제를 해결하기 보다는 JVM의 원자적인 특성을 이용해 초기화의 책임을 JVM으로 이동하는 방법
- 일반적으로 Singleton을 이용하는 방법
Spring에서는 이런 싱글톤 패턴을 통해 Bean에 대한 관리를 하는데 너무 복잡해지는 것을 방지하기 위해, Singleton Registry인 Application Context를 가지고 있다.
'CS > 운영체제' 카테고리의 다른 글
동기-비동기와 블럭-논블록의 차이 (0) | 2022.09.18 |
---|---|
프로세스와 스레드의 차이 (1) | 2022.09.11 |
컴퓨터 시스템의 동작 원리-3 (0) | 2021.10.28 |
컴퓨터 시스템의 동작 원리-2 (0) | 2021.10.28 |
컴퓨터 시스템의 동작 원리-1 (0) | 2021.10.28 |