멀티 쓰레드인 경우 서로 다른 쓰레드가 프로세스의 메모리를 공유하기 때문에 동시에 자원에 접근하는 상황(경쟁 상황)이 생기게 되는데 이런 경쟁 상황으로 인해 발생하는 문제가 동기화 문제이다.
이런 문제로부터 보호하기 위해 하나의 프로세스 혹은 하나의 쓰레드만이 한 자원에 접근할 수 있게 해야 한다.
Mutex(뮤텍스), Semaphore(세마포어) 방법을 사용하여 해결할 수 있다. 간단하게 말하면 뮤텍스는 한 개의 쓰레드만 공유 자원에 접근할 수 있게 해서 경쟁 상황을 방지하는 것이고, 세마포어는 지정한 수 만큼의 쓰레드만 공유 자원에 접근할 수 있게 하는 것이다.
우선, 뮤텍스와 세마포어에 알기 전에 임계영역에 대해서 알아야 한다. 임계영역은 둘 이상의 프로세스나 쓰레드가 동시에 동일한 자원에 접근하게 되는 프로그램 코드 부분을 의미하는데 각 프로세스나 쓰레드가 임계구역에서 일을 수행하는 동안 다른 프로세스나 쓰레드가 그 임계영역에 들어갈 수 없어야 한다. 즉, 임계영역 내의 코드는 원자적 실행이 되야 하는 것이다.
이렇듯, 원자적으로 실행되기 위해 임계영역으로 들어갈 때 entry section
을 통해 진입 허가를 요청하게 되고 허가 요청이 수락되면 임계영역을 실행하게 된다. 임계영역에서 수행이 끝나면 exit section
으로 퇴출하게 된다.
임계영역의 원자성을 보장함으로써 프로세스나 쓰레드들이 동기화 되도록 할 수 있고 이러한 방법 중 대표적인 방법이 뮤텍스와 세마포어인 것이다.
동기화 방법 중 하나로 공유 자원에 접근할 수 있는 프로세스 혹은 쓰레드를 1개로 제한한다.
쓰레드 A와 쓰레드 B가 있다고 가정한다.
1. 쓰레드 A가 공유 자원에 먼저 접근한다.
2. 공유 자원을 사용 중인 쓰레드 A는 다른 쓰레드가 접근하지 못 하도록 `lock`을 건다.
3. 이때, 쓰레드 B가 공유 자원에 접근한다.
4. 쓰레드 A가 걸어둔 `lock`에 의해 쓰레드 B는 공유 자원에 접근하지 못 하게 되고 쓰레드 A가 나오길 기다리면서 대기한다.
5. 쓰레드 A가 공유 자원을 다 사용하고 나오면서 `unlock`을 한다.
6. 이제, 기다리던 쓰레드 B가 공유 자원에 들어가게 되고 다른 쓰레드가 접근하지 못 하도록 'lock'을 건다.
이런 과정을 거치게 된다. 단점으로는 한 쓰레드가 자원을 사용하는 동안 다른 쓰레드는 자원에 접근하지 못 하므로 대기를 하게 되는데 이런 대기를 하는 상황 때문에 CPU를 낭비한다는 단점이 있다.
세마포어는 뮤텍스(단일)과 달리 여러 개의 쓰레드를 설정할 수 있다. 하지만 뮤텍스와 마찬가지로 단일 쓰레드만 공유 자원에 접근이 가능하도록 설정해 줄 수도 있는데 이러한 세마포어를 binary semaphore
라고 하고 이 경우 뮤텍스처럼 작동하게 된다.
세마포어는 여러 개의 쓰레드를 설정할 수 있다고 했는데 과정은 아래와 같다.
1. 정수형 변수 `s`(세마포)를 사용 가능한 자원의 수로 초기화한다.
2. 자원에 접근하면 `s--` 연산자를 통해 사용 가능한 자원의 수를 줄인다.
3. 자원을 다 사용하고 나가게 되면 `s++` 연산자를 통해 사원 가능한 자원의 수를 늘린다.
4. 세마포의 값이 `0`이 되면 모든 자원이 사용중인 것이기 때문에 세마포의 값이 `0`보다 커질 때 까지 대기해야 한다.
뮤텍스는 단일, 세마포어는 다수의 프로세스 혹은 쓰레드를 공유 자원에 접근 가능하도록 해준다. 하지만 세마포어를 단일 값으로 설정할 수 있는데 이 경우 바이너리 세마포어라고 하며 뮤텍스와 같다고 할 수 있다.