본문 바로가기

운영체제

멀티스레드의 개념

멀티스레드를 설명하기에 앞서 프로세스와 스레드부터 먼저 짚고 넘어가고자 한다.

 

프로세스와 스레드

프로세스 (Process)

프로세스는 현재 실행 중인 프로그램이다. 프로그램이 실행 중이지 않을 때는 보통 하드디스크에 저장되어 있다가 실행할 때 메인메모리로 이동한다. 이때 메모리는 다음 그림과 같이 구분된다.

프로세스 메모리할당 구조

 

 

  1. Code 영역: 프로그램 실행 코드를 위한 공간이다. 우리가 작성한 코드는 바로 여기에 있을 것이다. 코드영역은 읽기 전용이다.
  2. Data 영역: 전역변수 등 고정된 데이터를 위해 할당된 공간이다. Java의 경우 static 변수를 만들면 이쪽에 있을 것이다.
  3. Heap 영역: 동적할당 데이터를 위한 공간이다. Java의 경우 객체가 생성되면 이쪽에 저장이 될 것이다. 
  4. Stack 영역: 함수에 필요한 데이터를 위한 공간이다. 지역변수가 이쪽에 저장된다.

Code부터 Data까지는 불변 영역이지만 Heap부터 Stack은 가변적인 메모리 공간이다. 

Heap과 Stack은 사실 같은 메모리 공간을 사용한다. 하지만 같은 공간이라고 무작위로 저장이 되지 않고, Heap은 위에서부터 저장이되고 Stack은 아래서부터 저장이 된다. Heap이 Stack의 영역을 침범할 수도 Stack이 Heap의 영역을 침범할 수도 있는데 Heap -> Stack을 넘어설 때는 Heap Overflow, Stack -> Heap을 넘어설 때는 Stack Overflow가 발생한다.

 

프로세스가 여러개 실행된다면 메모리는 어떻게 할당될까?

멀티프로세스 메모리할당 구조

쓰레드 (Thread)

쓰레드는 하나의 프로세스 안에서 실행되는 실행 흐름 단위이다. 이게 무슨 말인지 처음에는 와닿지 않을 수 있다.

 

먼저 쓰레드가 왜 필요한지부터 알아야한다.

여러 사용자가 하나의 서비스에 동시에 접속할 때를 생각해보자. 동시 실행을 하려고 프로세스를 복사할 것이다. 그렇게 2개의 프로세스를 만들어서 실행하면 된다. 

 

하지만 이렇게하면 단점이 있다. 중복되는 영역들로 인해서 불필요한 메모리를 사용하게 된다. 여기에 또 다른 단점이 이보다 운영체제에게 더 큰 부담이 준다. 바로 컨텍스트 스위칭(Context Switching)이다. 컨텍스트 스위칭은 프로그램이 실행할 때마다 메인메모리에 올라와야하는데 프로세스를 실행시키기 위해서 메인메모리에 내리고 올리는 작업을 말한다. 이 때 프로세스 A를 내리고 프로세스 B를 올려야한다고 가정했을 때 프로세스 B는 메인메모리에 올리고 프로세스 A를 하드디스크에 옮겨야하는데 이때 시간이 오래걸리고 빨리 진행하는데 한계가 있다.

 

결국 이 문제를 극복하기 위해 쓰레드가 나왔다. 쓰레드에 메모리를 할당시킨다면 다음과 같다.

쓰레드 메모리할당 구조

 

Code 영역부터 Heap 영역까지는 모든 스레드가 공유하고 Stack 영역만 각각 사용한다.

쓰레드의 장단점

장점

  • 컨텍스트 스위칭 시 스레드 공유 영역(Code~Heap)은 올리고 내릴 필요가 없다.
  • Data와 Heap 영역을 스레드끼리 공유할 수 있어서 중복된 데이터만큼 자원(메모리)을 아낄 수 있다.

단점

  • 둘 이상의 쓰레드가 하나의 변수에 접근하려고 할 때 문제가 발생할 수 있다. (임계영역)
  • 쓰레드 하나가 프로세스 내의 자원을 망친다면 프로세스 종료될 수 있다. 프로세스가 종료되면 그 안에 실행되고 있던 스레드는 모두 강제종료된다.

 

쓰레드의 동시성과 병렬성

쓰레드 처리 방식 중에 동시성과 병렬성이 있다.

두개의 쓰레드를 처리해야한다고 가정하자.

 

동시성은 하나의 코어에서 두개의 쓰레드를 매우 빠른 속도로 마치 동시에 실행하는 것처럼 번갈아가면서 처리한다.

 

병렬성은 두개의 코어에서 각각 하나의 쓰레드를 맡아 진짜 동시에 처리하는 것을 말한다.

 

 

쓰레드의 동기화가 필요한 순간

여러 쓰레드가 동시에 공유하는 변수에 접근(데이터 변경)하려고 시도한다면 문제가 생길 것이다. 바로 여기가 임계영역이다. 이 문제를 해결하기 위한 것이 쓰레드 동기화(Synchronization)이다.

 

쓰레드 동기화가 필요한 순간은 다음과 같다.

  • 여러 쓰레드가 공유하는 자원에 동시 접근하려고 할 때
  • 쓰레드의 실행순서를 지정해야 할 때

두번째의 경우는 예를 들어 쓰레드 A가 데이터를 가져오는(저장) 역할이고 쓰레드 B가 데이터를 가져가는 역할이라고 가정해보자. 이럴 경우 쓰레드 B가 먼저 온다면 당연히 잘못된 결과로 이어질 수 있다.