해당 글은 스터디 모임 모함코의 네트워크 커리큘럼 과제물입니다.
우선 스레드의 개념을 이해하기 위해선 프로세스의 개념을 알아야한다.
1-1. 프로세스란?
- 프로세스란 단순히 실행중인 프로그램이라고 말할 수 있다.
- 즉 , 사용자가 작성한 프로그램이 디스크로부터 메모리에 적재되어 운영체제로부터 주소공간 메모리,파일 등을 할당받아 실행중인 것을 의미합니다. 자바에선 이를 JVM이 수행하고 있다.
- 함수의 매개변수, 복귀주소, 로컬(인스턴스) 변수와 같은 임시 자료들을 저장하는 프로세스 “스택”과 전역 변수를 저장하는 데이터 섹션, 프로세스 실행중에 동적으로 할당되는 메모리인 “힙”을 포함한다.
- 이러한 프로세스는 프로그램에서 사용되는 데이터와 메모리등의 자원 , 스레드로 구성된다.
1-2. 스레드란?
- 스레드란 프로세스 내부에서 실제로 작업을 수행하는 주체를 의미한다.
- 모든 프로세스에는 반드시 하나 이상의 스레드가 존재하여 작업을 수행한다.
- 그렇기 때문에 프로세스가 할당받은 자원을 이용하는 실행의 단위이며 하나의 프로세스에서 여러개의 스레드가 일을 할 수 있다. EX) 하나의 크롬 브라우저(=프로세스)에서 유튜브(=스레드1)를 보며 티스토리에 블로그를 적는(=스레드2)것
- 한 프로세스 내에서 동작되는 여러 실행 흐름으로 프로세스 내의 Heap,Data,Code 영역을 공유하는 역할도 있다.
- 그럼 멀티스레드란? 두개이상의 스레드를 가지는 프로세스를 정의하는 말이며 하나의 프로세스가 다수의 실행단위인 스레드로 구분하여 자원을 공유하며 자원의 중복성을 최소화하여 수행능력을 향상시킨 것을 멀티스레드라고 한다.
- 각각의 스레드는 독립적인 작업을 수행해야하므로 고유한 식별ID 및 PC,레지스터를 가지고 있다.
그럼 이제 싱글스레드와 멀티스레드에 대해 자세히 알아보도록 하기전에 이 사진을 본다면 개념의 이해가 좀 더 쉽게 다가올 것이다.
방금 스레드의 설명과 멀티스레드에 대한 설명까지 들으면서 한가지 의문점을 우린 가져야한다.
싱글스레드 보다 멀티 스레드가 당연히 좋은 것 같은데 왜 다중 스레드를 무조건적으로 사용하지 않을까? 나는 개인적으로 멀티 스레드가 더 좋다고 생각은 하나 모든 기술에 이를 적용하기에는 기회비용적인 측면에서 불가능하며 각각의 장점이 명확히 존재하기에 그에 대해 설명하려한다.
멀티스레드
멀티스레드는 CPU의 최대 활용을 위해 하나의 프로세스에 여러 스레드를 가동시키는 것이다.
이러한 작업은 컨텍스트 스위칭을 통해서 이뤄진다. 위의 이미지에서 하나의 스레드에서 다음 스레드로 이동하며 컨텍스트 스위칭이 일어나며 부분적으로 스레드에 대한 작업을 실행한다.
다시 말해 빠른 속도의 Context Switching이 일어나기 때문에 프로그램이 동시에 수행되는 것으로 보여진다.
멀티스레드에서 동시성과 병렬성
흔히들 말하는 동시성이란 멀티스레드를 동작시키기 위한 방식으로 태스킹을 위해 여러 스레드가 번갈아가며 실행되는 성질을 뜻한다. 병렬적으로 실행되는 것이 아닌 사실은 번갈아 가며 조금씩 실행되는 것이다.
병렬성은 멀티코어에서 멀티스레드를 동작시키는 방식으로 한 개 이상의 스레드를 포함하는 각 코어들이 동시에 실행되는 성질을 일컫는다. 병렬성은 데이터 병렬성과 작업병렬성으로 구분된다.
데이터 병렬성은 전체 데이터를 잘게 쪼개어 서브데이터를 만들어 병렬 처리하는것을 뜻한다. 자바의 스트림과 같은 원리이며 멀티코어의 수만큼 쪼개어 각각의 데이터를 분리된 스레드에서 처리한다.
작업 병렬성은 서로 다른 작업을 병렬 처리하는 것을 말한다. 예시로 브라우저를 들 수 있다.
멀티프로세스와 멀티스레드의 차이
- 동시에 두 가지 이상의 루틴을 수행할 수 있다.
멀티프로세스(Multi-Process)
- fork를 통해 프로세스를 복사할 수 있다. 또한 부모 자식 관계에 속하더라도 환경변수와 프로세스 핸들 테이블이 상속 가능할 뿐, 자신만의 메모리 영역을 가진다.
- 프로세스간의 통신을 하려면 IPC를 통해야 한다.
멀티스레드
- 단일 프로세스의 컨텍스트 내에서 여러 스레드를 동시에 실행하는 것.
- 한 프로세스 내의 스레드들은 Code, Data, Heap 영역을 공유한다.
멀티 스레드와 멀티 프로세스의 가장 큰 차이점으로는 멀티프로세스는 하나의 프로세스가 죽더라도 다른 프로세스에 영향을 주지 않아 안정성이 높지만 멀티 스레드의 경우에는 하나의 스레드가 고장나면 자원을 공유중인 스레드 모두가 고장나게된다.
그렇다면 왜 멀티 프로세스로 할 수 있는 작업들을 굳이 하나의 프로세스에서 스레드로 나눠가며 할까?
- 운영체제가 시스템 자원을 효율적으로 관리하기 위해 스레드를 사용한다.
- 멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행할 경우, 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리할 수 있다.
- 또한, 프로세스 간의 통신보다 스레드 간의 통신 비용이 적으므로 작업들 간 통신의 부담이 줄어든다. (처리비용 감소. 프로세스는 독립구조이기 때문)
🎇 멀티스레드의 장점
- 응답성: 프로그램의 일부분이 중단되거나 긴 작업을 수행하더라도 프로그램의 수행이 계속 되어 사용자에 대한 응답성이 증가하게된다.
다시말해 멀티 스레드 모델은 에러 발생 시 새로운 스레드를 생성하여 극복한다. 다만 , 스레드를 새롭게 생성하거나 잉여 스레드에 대해처리할 때 비용이 발생한다.
여기서 싱글스레드와 가장 큰 차이점이 발생하게 되는데 싱글 스레드의 경우 스레드에 문제가 발생하게 되면 프로그램이 멈추게 된다. - 경제성 : 프로세스 내의 자원들과 메모리를 공유하기 때문에 메모리 공간과 시스템 자원 소모가 줄어들게 된다. 스레드 간 통신이 필요한 경우에도 쉽게 데이터를 주고 받을 수 있고 프로세스의 Context Switching과는 다르게 스레드 간의 Context Switching은 캐시 메모리를 비울 필요가 없어 빠르다.
- 멀티프로세서의 활용 : 다중 CPU 구조에서는 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있으므로 병렬성이 증가한다.
⚠️ 멀티스레드의 단점
- Context Switching, 동기화 등의 이유로 싱글 코어 멀티스레딩은 스레드 생성 시간이 오히려 오버헤드로 작용해 단일 스레드 보다 느리다.
- 공유하는 자원에 동시에 접근하는 경우 프로세스와는 달리 스레드는 데이터와 힙 영역을 공유하기 때문에 어떤 스레드가 다른 스레드에서 사용 중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽어오거나 의도치 않은 수정이 일어날 수 있다. 이를 임계 영역에서의 동기화 문제라고한다.
- 멀티 스레딩을 위해서는 운영체제의 지원이 필요하다.
- 동기화를 통해 스레드의 작업 처리 순서와 공유 자원에 대한 접근을 컨트롤할 수 있다. (Java에서 synchronized 키워드) 그러나 불필요한 부분까지 동기화를 하는 경우, 과도한 lock으로 인해 병목 현상을 발생시켜 성능이 저하될 가능성이 높기 때문에 주의해야 한다. 동기화 방법에는 뮤텍스와 세마포어가 있다.
- 멀티 스레드 모델은 프로그래밍 난이도가 높고 자원을 많이 잡아 먹는다. 자원을 많이 잡아 먹는다는 것은 스레드의 수가 많아질수록 프로그래밍의 난이도가 올라가게 되어 실력차에 따라 자원 활용도가 편이하다.
뮤텍스와 세마포어
동시성 프로그래밍의 가장 큰 숙제 중 하나인 공유자원 관리에 대해서는 안정성을 중요시하기에 상호배제를 달성하는 기법을 필요로 하며 뮤텍스와 세마포어를 서로다른 방식으로 상호배제를 달성하기 위해 고안되었다.
뮤텍스 : 한 쓰레드와 프로세스에 의해 소유될 수 있는 Key🔑를 기반으로한 상호배제기법
세마포어 : 현재 공유자원에 접근할 수 있는 쓰레드 또는 프로세스의 수를 나타내는 값을 두어 상호배제의 목적을 달성한다.
뮤텍스 자세한 예시
뮤텍스는 화장실이 하나 뿐이 없는 식당과 비슷하다. 화장실을 가기 위해서는 카운터에서 열쇠를 받아 가야 한다.
당신이 화장실을 가려고 하는데 카운터에 키가 있으면 화장실에 사람이 없다는 뜻이고 당신은 그 열쇠를 이용해 화장실에 들어갈 수 있다.
이때 다른 사람이 화장실을 이용하고 싶다면 그사람은 카운터에서 기다려야한다.
곧이어 옆 테이블에 있는 남자도 화장실에 가고 싶어졌고 이 남자 또한 화장실에 들어가기 위해서는 카운터에서 대기해야한다.
이제 여성이 화장실 이용을 끝마치고 나가게되면 그다음 대기자가 화장실을 이용할 수 있다.
이것이 뮤텍스가 동작하는 방식이다. 화장실을 이용하는 사람은 프로세스 혹은 쓰레드이며 화장실은 공유자원, 화장실 키는 공유자원에 접근하기 위해 필요한 어떤 오브젝트이다.
즉 뮤텍스는 Key 에 해당하는 어떤 오브젝트가 있으며 이 오브젝트를 소유한 (쓰레드,프로세스) 만이 공유자원에 접근할 수 있다.
세마포어 자세한 예시
세마포어는 손님이 화장실을 좀 더 쉽게 이용할 수 있는 레스토랑이다. 세마포어를 이용하는 레스토랑의 화장실에는 여러 개의 칸이 있다. 그리고 화장실 입구에는 현재 화장실의 빈 칸 개수를 보여주는 전광판이 있다.
만약 당신이 화장실에 가고 싶다면 입구에서 빈 칸의 개수를 확인하고 빈 칸이 1개 이상이라면 빈칸의 개수를 하나 뺀 다음에 화장실로 입장해야 한다. 그리고 나올 때 빈 칸의 개수를 하나 더해준다.
모든 칸에 사람이 들어갔을 경우 빈 칸의 개수는 0이 되며 이때 화장실에 들어가고자 하는 사람이 있다면 빈 칸의 개수가 1로 바뀔 때까지 기다려야 한다.
사람들은 나오면서 빈 칸의 개수를 1씩 더한다. 그리고 기다리던 사람은 이 숫자에서 다시 1을 뺀 다음 화장실로 돌진한다.
싱글스레드
하나의 프로세스당 하나의 스레드만이 부여되며 하나의 레지스터와 스택으로만 표현이 가능하다.
🎇 싱글 스레드의 장점
- Context Switch 작업을 요구하지 않는다. 고로 많은 비용을 필요로 하지않아 싱글코어 상에서는 상당히 빠른 속도를 보장한다.
- 자원접근에 대한 동기화를 신경쓰지 않아도 된다.예를 들어 모든 스레드가 동시에 동일 자원에 접근하거나 똑같은 작업을 실행할 경우 멀티스레드의 경우 에러가 발생하거나 원하지 않는 값이 나올 수 있다.
- 하지만 싱글 스레드는 이를 생각하지 않아도 된다. 해당 작업은 멀티스레드의 경우 많은 자원과 노력을 들여 프로그래머가 구현해야하지만 싱글스레드는 설계단계부터 고려하지 않아도 되기에 프로그래머의 편의성이 높아진다.
- 여러개의 스레드가 프로세스의 자원을 공유할 경우, 각 스레드가 원하는 결과를 얻게하려면 공용 자원에 대한 접근을 제어해야한다.
- 단순 CPU만을 이용하는 계산작업의 경우 싱글스레드의 효율성이 더 높다. 단순 CPU를 사용하는 계산의 경우 단일 스레드의 효율성이 좋은 이유는 멀티스레드의 경우에는 Context Switch가 일어나 시간의 효율성이 떨어지기 때문이다.
- 프로그래밍 난이도가 쉽고 CPU 메모리를 적게 사용한다.
⚠️ 싱글 스레드의 단점
- 여러개의 CPU를 활용하지 못한다. 다중 코어를 활용하지 못한다.싱글스레드의 장점을 살리기 위해선 서버 프로그램 인스턴스간에 상태 공유를 최소화하는 또는 공유를 하지않는 아키텍쳐로 설계하는 것이 최선이다.
- 프로세서를 최대한 활용하기 위해선 cluster 모듈을 사용하거나 외부에서 여러개의 프로그램 인스턴스를 실행시키는 방법을 사용해야한다. 하지만 인스턴스를 사용하더라도 다수의 인스턴스간에 자원 공유 또한 고려해야하는 문제가 생긴다.
- 연산량이 많은 작업의 경우 작업이 완료되어야 이후 작업을 수행할 수 있다.
- 예를 들어 웹 노트 프로그램이 있다고 가정해보자. 그 노트 프로그램에서 그림을 그릴 때 현재 마우스의 좌표값을 받는데 0.1초가 든다고 가정하자 이를 선을 긋는 프레임당 0.1초라고 생각하였을때 그림을 다그리는데 5분정도가 소요된다고 하였을때 대략 30초정도는 온전한 서비스의 동작을 보장받지 못한다.
- 싱글 스레드 모델은 에러 처리를 못하는 경우 멈추게 된다.
- 멀티 스레드의 경우 에러를 만날 경우 새로운 스레드를 생성하여 에러에 유연하게 대처가 가능하지만 싱글 스레드는 멈추게된다.
Node.js는 싱글 스레드일까?
아니다 자바스크립트의 런타임인 Node.js는 멀티스레드로 구성이 되어있다. 왜 그런것일까?
Node.js는 V8이라는 자바스크립트 엔진과 libuv라는 비동기처리 라이브러리로 이루어져있다.
이때 libuv는 C++로 개발이 되어 있기 때문에 멀티스레드로 동작은 가능하지만 JS라는 싱글스레드의 장점을 가지고 있는 언어의 특성을 살리기 위해 싱글스레드처럼 동작한다.
컨텍스트 스위칭
멀티프로세스 환경에서 CPU가 어떤 하나의 프로세스를 실행하고 있는 상태에서 인터럽트 요청에 의해 다음 우선 순위의 프로세스가 실행되어야 할 때 기존의 프로세스의 상태 또는 레지스터 값(Context)을 저장하고 CPU가 다음 프로세스를 수행하도록 새로운 프로세스의 상태 또는 레지스터 값(Context)를 교체하는 작업을 Context Switch(Context Switching)라고 한다.
'ComputerScience' 카테고리의 다른 글
🛠 프로그래밍 패러다임 (1) | 2023.02.26 |
---|---|
[CS] GoF 디자인 패턴 -2 (0) | 2022.12.27 |
[CS] GoF 디자인 패턴 -1 (0) | 2022.12.20 |
[CS] 시간복잡도와 공간복잡도 (0) | 2022.12.06 |