❓Concurrency란 뭘까?
운영체제에서 Concurrency란 상당히 중요한 개념이다.
현대의 컴퓨터는 기본적으로 멀티코어 프로세서를 가지고 있기에 작업의 병렬 처리가 가능하다.
웹 브라우저에서 여러 웹 페이지를 동시에 로드하거나, 편집기에서 여러 문서를 동시에 편집하거나 등등.
즉 concurrency(동시성) 란, 한 번에 여러 작업을 수행할 수 있는 컴퓨터 시스템의 능력을 가리키는
개념이다. OS로부터 각각 자원을 할당 받는 프로세스를 여러개 두고 관리하거나, 한 프로세스에서 자원을
공유하는 여러 스레드를 생산해 멀티 쓰레딩 프로그래밍을 하거나 할때에, concurrency는 빛을 발한다.
하지만 concurrency는 상당히 예민한 개념이다.
한 광산(프로세스)에서 여러명의 인부(스레드)가 작업을 하다보니, 서로 같은 데이터를 두고 다투는 경우가
일어날 수도 있기 때문이다. 이를 두고 Race Condition 이라는 개념이 등장한다.
❓Race Condition이란?
앞의 예시를 다시 들어보자.
한 광산에서 두 인부가 일하고 있었다. 광부들은, 회사에서 임무를 부여받고 광산으로 일을 하러간다.
회사에서는 게시판에 다음과 같은 작업을 공고했다.
즉 인부 A와 B는 합쳐서 5개의 광물만을 캐면 퇴근을 할 수 있는것이다!
이를 보고 인부 A와 B는, 약간의 시간차를 두고 각자 광산으로 출근을 했다.
인부 A는 현재 광물이 5개라는 사실만을 안 채로, 광물을 3개 캔 뒤 회사에
'현재 광물의 개수는 8개입니다' 라고 무전한다.
하지만 애석하게도, 간발의 차로 인부 B도 본사로부터 현재 광물이 8개라는 사실을 전해듣기 전에 5개라는
사실만을 안 채로, 광물을 2개 캔뒤 본사에
'현재 광물의 개수는 7개입니다'라는 무전을 한 것이다.
본사는 무전을 받는 족족 업데이트를 하는데, '현재 광물의 개수는 8개입니다' 무전을 받은 뒤 광물의 개수를 8개라고 업데이트를 한다. 하지만 바로 직후 '현재 광물의 개수는 7개입니다' 무전을 받고, 현재 광물을 10개가 아닌 7개로 업데이트 하고 마는것이다.
이를 코드로 한 번 살펴보자.
위 코드에서는 main 함수(회사)에서 pthread_create 함수로 p1과 p2 두개의 thread(광부)를 생성하고,
counter(광물)를 각각 1000000 만큼 증가시키는 '임무'를 맡긴다.
첫번째 경우처럼 인부 두명이 작업을 한 뒤 무전도 교차적으로 잘 이루어졌다면, 결과는 정상적으로
'2000000'개의 광물이 있다고 인지하겠지만,
앞서 말한 예시처럼 무전이 교차적으로 잘 일어나지 않았다면, 두번째 세번째 경우처럼 총 개수에
오차가 생기고 마는 것이다. 이를 두고 OS에서는 Race Condition이 일어났다고 한다.
이렇게 혼동이 일어날 수 있는 data 영역을 두고, Critical section 이라고 일컫는다.
그리고 Critical section에서 발생하는 Race Condition을 방지해주는 것을
Mutual exclusion (상호 배제)라고 한다.
Mutual exclusion을 구현하기 위해서는
- 세마포어 (semaphore)
- 뮤텍스 (Mutex)
- 모니터 (Monitor)
등의 기법을 사용할 수 있다.
❓Mutex란?
Mutex는 공유 자원에 대한 접근을 동시에 하나의 스레드만을 허용하게 한다.
Mutex는 lock과 unlock 연산을 사용해서, 내가 어떤 공유 자원을 사용하고자 할 때 다른 thread가
접근할 수 없도록 공유 자원에 대한 접근을 시도하는 critical section에 lock을 하고 연산을 한 뒤,
unlcok하여 다른 thread가 접근 가능하도록 잠금을 해제해준다.
위는 C에서 간단히 lock과 unlock을 사용하여 counter 공유 자원에 대해 하나의 thread만이
접근 가능하도록 구현한 스도코드이다.
ptread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER
위 코드가 C에서 Mutex를 선언하는 방법이다.
pthread_mutex_unlock(&lock);
int pthread_mutex_destroy(&lock);
사용이 끝난 Mutex는 위와 같은 destroy 함수로 삭제할 수 있다.
하지만 mutex를 사용중에 해제할 수도 있기 때문에 사용중인 mutex가 있다면
반드시 먼저 unlock 해주어야한다.
* 추가로, pthread를 사용하는 프로그램을 컴파일 할때는 반드시 마지막에 -pthread 옵션을 주어야 한다.
ex)
gcc -o main main.c -pthread