diff --git a/contents/language/README.md b/contents/language/README.md index 46628b7..e1b8a9a 100644 --- a/contents/language/README.md +++ b/contents/language/README.md @@ -7,3 +7,5 @@ - 자바 API 활용 ## C++ + +- [멀티스레드 프로그래밍](./c++/multithread-programming.md) \ No newline at end of file diff --git a/contents/language/c++/multithread-programming.md b/contents/language/c++/multithread-programming.md new file mode 100644 index 0000000..e2b0dcf --- /dev/null +++ b/contents/language/c++/multithread-programming.md @@ -0,0 +1,156 @@ +# Multithread Programming + +> 작성자 : [박재용](https://github.com/ggjae) + +본 자료는 작성자인 '박재용'의 개인 Repository의 글을 재구성하여 작성되었습니다. +자료를 공부하기 이전에 익혀두어야 하는 두가지 기술을 먼저 이야기하겠습니다. + +- 프로세스와 스레드가 무엇인지 이해하기 +- 스레드와 프로세스를 구분해보기 + +
+Table of Contents + +- [프로세스 Review](#프로세스) +- [스레드 Review](#스레드) +- [멀티스레드 프로그래밍의 사용 이유](#멀티스레드-프로그래밍의-사용-이유) +- [멀티스레드 프로그래밍이 힘든 이유](#멀티스레드-프로그래밍이-힘든-이유) +- [병렬 프로그램](#병렬-프로그램) + +
+ +--- + +## 프로세스 + +운영체제가 관리하는 프로그램의 단위 +실행파일을 실행하는 것이 곧 프로세스를 만드는 것. 실행파일의 실행은 운영체제가 파일을 읽어서 메모리에 복사해두고 시작 주소로 점프하는 것. 멀티코어가 아니더라도 여러개의 프로그램이 동시에 실행된다. + +프로세스의 메모리 구조 + +- Code : 실행될 명령어가 들어가는 구역 +- Data : 전역 변수가 들어가는 구역 +- Stack : 지역변수와 함수 리턴 주소가 들어가는 구역 +- Heap : malloc이나 New로 할당받은 메모리가 들어가는 구역 +- PCB : Process Control Block + +프로세스는 내부적으로 성격에 따라 여러개의 구역으로 나누어서 관리를 하는 것을 **세그먼트**라 한다. + +## 스레드 + +프로그램 내에서의 실행되는 흐름의 단위 +프로세스의 부분집합이다. 모든 스레드는 자신 고유의 스택을 가지고 있고 Data, Heap, Code 영역은 다른 스레드와 공유하게 된다. + +한 프로그램에서의 여러개의 스레드를 사용한다면 코드와 데이터 힙 영역은 스레드간 공유가 된다. 똑같은 메모리를 같이 사용하고 한 장소에 있는 것을 자식과 부모가 같이 사용한다고 생각하면 된다. + +--- + +## 프로세스와 스레드 관련 + +- 스택영역만 새로 하나 파주면 되기 때문에 스레드는 생성 시 overhead가 프로세스보다 작다. +- 캐시 미스가 비교적 적게 일어나기 때문에 스레드는 Context Switching 비용의 overhead가 프로세스보다 적다. +- 스레드간의 통신이 프로세스간의 통신보다 간단하다. 프로세스간의 통신은 overhead가 큼 +- 하나의 스레드에서 발생한 문제가 전체 프로세스를 멈추게 한다. +- 하나의 프로그램에서 여러군데가 동시에 실행되므로 스레드의 경우 디버깅이 너무나도 어렵다. + + +# 멀티스레드 프로그래밍의 사용 이유 + +- 멀티코어 CPU에서의 프로그램 성능 향상을 위하여 +- 멀티 CPU 컴퓨터, 병렬컴퓨터에서의 프로그램 성능 향상을 위하여 + + +하나의 프로그램을 더 빠르게 돌리기 위해서 사용한다. +10 FPS 게임을 20 FPS로 올리고 싶을 때, 처리량을 높이기 위하여 **멀티스레드** 프로그래밍을 한다. + + +## 멀티스레드 프로그래밍이 뭐야? + +- 병렬 프로그래밍의 유일한 구현 수단 +- 하나의 프로세스 안에서 여러개의 스레드를 실행시켜 병렬성을 얻는 프로그래밍 방법 + + +## 성능을 위하여 멀티스레드 프로그래밍을 하는가? + +정답은 아니다. 게임은 성능이 중요하기 때문에 상관은 있지만 게임이 느리거나, 프로그램이 느리다면 성능 개선이 우선시 되어야 한다. 다양한 자료구조와 알고리즘을 통해서 성능개선을 진행해야 하고 최후의 보루로 멀티스레드 프로그래밍을 진행하여야 한다. +**주의** 멀티스레드 프로그래밍을 하면 더 느려질지도 모른다. + +## 멀티스레드 프로그래밍이 힘든 이유 + +매번 같은 코드를 실행함에도 결과값이 달라진다. 우리가 프로그래밍해서 잘못된 결과면 계속 같은 잘못된 결과가 나와야 하는데 멀티스레드 프로그래밍은 값이 매번 다르다. 이것은 잘못 만든 프로그램으로 디버깅이 쉽지 않고 Data Race에 부딪힐 수 있다. +Data Race란 같은 데이터를 두개의 스레드가 동시에 읽고 쓰고 할 때에 모든 전역 변수는 공유 메모리이므로 읽고 쓰는 순서에 따라 실행 결과가 달라지고 이 상태를 경쟁 상태라고 한다. 하지만 C++11에서 lock과 unlock을 지원해주므로 복수개의 스레드가 동시에 접근할 수 없도록 코딩을 진행하여야 한다. + +``` CPP +#include +#include +#include + +std::mutex mtx_lock; + +int main(){ + + std::thread Threads1([&] (){ + for(int i=0;i<5;++i){ + mtx_lock.lock(); + std::cout << "Thread Num : " << i << std::endl; + mtx_lock.unlock(); + } + }); + + std::thread Threads2; + Threads2 = std::thread([&](){ + for(int i=10;i<15;++i){ + mtx_lock.lock(); + std::cout << "Thread Num : " << i << std::endl; + mtx_lock.unlock(); + } + } + return 0; +} +``` + +멀티스레드 프로그래밍 시 mutex 객체는 전역 변수로 코딩하여야 하고 같은 객체 사이에서만 lock과 unlock이 동작하는 것을 주의하여야 한다. 서로 동시에 실행되어도 괜찮은 Critical Section (lot reader, 0 writer)에서는 다른 mutex 객체로 보호하는것이 성능에 좋다. + +## 멀티코어 CPU + +**한 개 이상의 코어**로 구성된 CPU. i3, i5, i7 +2021년 현재는 모든 것이 다 멀티코어 ex) Xbox, 닌텐도 스위치 + + +## C++ 언어에 멀티스레드 라이브러리가 표준으로 존재 + +과거의 멀티스레드 프로그래밍 방법: +Window에서는 Win32 라이브러리에서 지원되는 API 사용했었고, 윈도우는 멀티스레드에 특화된 OS이고 리눅스는 pthread API를 사용하면 가능했다. + +Windows에서의 thread => +프로세스의 하위 개념 프로세스는 처음 시작 시 한개의 스레드를 갖고 시작되고 운영체제가 직접 스케쥴링하게 된다. + +--- + +## 병렬 프로그램 + +병렬 프로그램의 특징 +- 실행된 프로세스 내부의 여러곳이 동시에 실행된다. +- 병렬로 실행되는 객체 사이의 동기화가 필수이다. (synchronization) +- **공유메모리 모델**과 메세지패싱 모델이 있다. + +## 병렬 프로그램의 요구사항 + +- 정확성 : 다양한 흐름에서 동시다발적으로 호출해도 문제 없이 실행되는 알고리즘이 필요 +- 성능 : Context 증가에 따른 성능 향상이 높아야 함 + +--- + +### 자주 묻는 질문 + +1. **멀티코어 프로세서**를 만드는 이유? + +> CPU의 성능을 올려야하고, 클럭 속도를 높여야 한다. 하지만 클럭 속도를 높일 수 없음. 한계가 정해져 있다. => 클럭 속도가 4GHz가 되면 컴퓨터가 불에 타게 된다. 우주가 그렇게 설계되어 있음. +4Ghz의 벽이라고도 한다. 사용하려면 실시간으로 액체질소를 들이부어야 함. 남은 것은 멀티코어 뿐이였고, 싱글로는 도저히 안되니까 코어 개수로 싸우고 있다. 듀얼코어 CPU 2개, 쿼드코어 CPU 1개 차이는 크지 않다. 여러 코어가 늦게 개발된 이유는 프로그램을 다시 짜야하는 번거로움뿐만 아니라 디버깅도 어렵고 이미 작성한 알고리즘도 사용하지 못하기 때문이다. + +2. 스레드의 개수와 코어의 개수는 일치하지 않아도 상관이 없는 이유? + +> 프로세스가 시분할로 돌아가면서 실행되듯 스레드도 시분할로 운영되어 중간에 다른 스레드가 실행될 수 있다. 쉽게 코어의 개수는 CPU 내 물리적 연산부의 개수이고, 스레드는 **작업단위**이므로 일치하지 않아도 된다. + +3. 멀티스레드 프로그래밍을 진행했을 때 싱글스레드 프로그래밍보다 느려졌다. 그 이유는 무엇일까? +> 사용하는 스레드가 많아지면 많아질수록 bottleneck의 가능성과 쓰레드마다 참조하는 캐시가 다르므로 불일치 문제가 생기거나 스레드 하나만으로도 진행할 수 있는 간단한 코드를 여러 스레드로 진행시켜도 race condition을 기다리기 위해 시간이 사용되곤 한다. 적재적소에 사용하여야 한다.