Mutex
- Mutex는 Mutual exclusion(상호 배제)의 약자이다.
- 어떠한 값을 두 개의 스레드가 동시에 참조(변경)하려고 하면 문제가 생길 수 있다.
- 그러므로 동시에 참조하는 것을 막는 것이 Mutex이다.
- 만약 코드에서 두 개의 스레드가 동시에 참조하려고 해도, Mutex를 통해 한 번에 하나씩 스레드가 참조하며, 다른 것이 참조하려고 하면 기다리게 하거나 막는 방식으로 차단한다.
- Mutex 변수를 통해 스레드들간에 동기화를 이룰 수 있으며, 공유 데이터를 보호할 수 있다.
- Mutex 변수는 자물쇠같은 역할을 하며, 다음과 같이 작동한다.
- 먼저 요청한 스레드에게만 데이터를 열 권한을 준다.
- 이 동한 다른 스레드는 waiting queue에 들어가 있게 된다.
- 스레드가 작업을 마치면 데이터를 다시 연다.
- 데이터가 다시 열렸으므로 요청한 스레드에게 다시 권한을 준다.
- 오직 하나의 스레드만 lock을 얻을 수 있다.
- 멀티 스레드를 사용할 때 Mutex는 데이터의 보호를 위해서 굉장히 중요하다.
Mutex Example
- Mutex가 필요한 예시를 보자.

- 어떤 사람의 은행 계좌가 있다.
- 은행 계좌에는 원래 1000$가 있다.
- 이 사람이 포스기에서 자신의 은행 계좌에 200$를 입금하려고 한다.
- 그와 동시에 은행 계좌에 이자가 발생헤서 은행사에서 이자 200$를 주는 상황이다.
- 이것이 각각의 스레드로 이루어진다.
- 두 스레드는 계좌의 잔액을 갱신하려고 한다.
- 원래는 1400$ 가 되어야 한다. 그러나 운이 나쁘면 1200$가 될 수 있다.
- 스레드 1이 원래 계좌에 남아있는 돈을 불러온다. 이는 1000$ 이다.
- 이와 동시에 스레드 2가 원래 계좌에 남아있는 돈을 불러온다. 이는 1000$이다.
- 스레드 2가 local에 계산한 값은 1200$이다.
- 스레드 1이 local에 계산한 값은 1200$이다.
- 이제 스레드 1이 원래 계좌를 갱신한다. 이는 1200$이다.
- 마지막으로 스레드 2가 원래 계좌를 갱신한다. 이는 1200$이다.
- 이렇게 되면 은행이 고소먹고 망한다.
- 어디가 잘못된 걸까? 두번째부터 잘못됐다. 계좌에 남아있는 돈을 불러올 때 1000$ 가 아닌 1200$를 불러와야 알맞게 계산된다. 이러한 문제를 해결하려면 먼저 수행되는 스레드가 종료할 때 까지 기다리는 과정이 필요하다.
Race Condition
- 프로그램상에서도 멀티 스레드로 인한 문제가 발생할 수 있다.

- 두 스레드는 count라는 공유 변수에 접근할 수 있다. 이 스레드가 동시에 count에 접근하는 상황을 보자.
- count의 초기 값은 5이다.
- 스레드 1은 count값을 하나 증가시킨다.
- 스레드 2는 count값을 하나 감소시킨다.
- 스레드 1과 스레드 2를 각각 한번씩 실행시키면 결과값은 5가 되어야 할 것이다.
- 아래는 오류상황이다.
- 스레드 1이 count값을 자신의 레지스터에 저장한다. 값은 5이다.(read)
- 스레드 1이 레지스터 값을 하나 증가시킨다. 값은 6이다.(update)
- 스레드 2가 count값을 자신의 레지스터에 저장한다. 값은 5이다.(read)
- 스레드 2가 레지스터 값을 하나 감소시킨다. 값은 4이다.(update)
- 스레드 1이 count값을 자신의 레지스터 값으로 갱신한다. 값은 6이다.(write)
- 스레드 2가 count값을 자신의 레지스터 값으로 갱신한다. 값은 4이다.(write)
- 결과값이 5가 아닌 4가 되었다. 굉장히 큰(숫자적으로는 작지만) 오류이다.
Mutex를 구현하는 방법
- Mutex는 여러 스레드가 전역 변수에 접근하는 과정에서 꼭 필요한 조치이다.
- 이 전역 변수를 "critical section" 으로 만들어줘야 한다.
- Mutex를 구현하는 방법은 아래와 같다.
- Mutex타입의 변수를 선언하고, 초기화해준다.
- Mutex변수에 접근하려는 모든 스레드는 lock을 얻으려는 요청을 해야한다.
- 만약 동시에 lock을 요청하면, 맨 처음에 요청한 스레드 하나에게만 lock을 준다. 나머지 스레드는 Mutex변수 자체가 가지고 있는 waiting queue에 들어가게 된다.
- lock을 가진 스레드는 Mutex변수를 가지고 작업을 수행한다.
- 다른 스레드는 대기하게 된다.
- lock을 가진 스레드가 작업을 모두 마치면, "무조건" lock을 해제해야 한다.
- lock이 해제되면, waiting queue에서 다음 스레드에게 lock을 주게 된다. 반복한다.
- Mutex변수를 모두 사용하면 없애준다.
- 이런 과정을 잘 구현하도록 POSIX는 여러 함수를 구현해 놓았으며, 프로그래머는 다중 스레드를 구현하려면 이 함수들을 잘 활용해서 Mutex를 구현해야한다.
Creation / Initialization
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- Mutex 변수는 선언하고 무조건 초기화를 해주어야 한다.
- pthread_mutex_t
- mutex lock을 나타내는 Mutex 변수의 타입이다.
- 항상 사용하기 전에 초기화를 해주어야 한다.
- Mutex 변수의 초기화는 두가지 방법이 존재한다.
- PTHREAD_MUTEX_INITIALIZER : 간단하게 기본값으로 초기화한다. 주로 static값으로 선언된 변수를 초기화할때 사용한다.
- int pthread_mutex_init : 변수를 이용하여 Mutex값을 초기화한다. 속성과 같이 초기화하므로 내가 원하는 특정 값을 Mutex 변수의 초기 값으로 설정할 수 있다는 장점이 있다.
- pthread_mutex_t *restrict mutex : 초기화하려는 Mutex 변수의 포인터 값이다.
- const pthread mutexattr_t *restrict attr : Mutex 변수를 초기화 할 때 설정할 속성이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 초기화를 여러번 하는 경우는 정의되지 않았으므로, 한번만 할 수 있도록 해야한다.
Destroy
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- Mutex 변수는 사용이 끝나면 동적 변수와 비슷하게 해제해줘야 한다.
- pthread_mutex_destroy로 Mutex 변수를 해제할 수 있다.
- pthread_mutex_t *mutex : 해제할 Mutex 변수이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 스레드가 이미 해제된 mutex 변수를 참조하는 것을 정의하지 않았으므로, 어떤 오류가 생길 지 모른다. 그러므로 mutex 변수를 참조하는 스레드가 없는지 확인하고 해제해야 한다.
- 스레드가 mutex 변수를 사용중인데 해제하면, 어떤 오류가 생길 지 모른다. 그러므로 mutex 변수를 사용하는 스레드가 없는지 확인하고 해제해야 한다.
Locking / Unlocking
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);s
- pthread_mutex_lock : OS에게 mutex 변수에 대한 lock을 요청한다. - 만약 2개 이상의 스레드가 동일 mutex 변수에 대한 lock을 요청하면 하나의 스레드에만 lock을 주고 해제될 때 까지 mutex변수를 block한다.
- lock을 받지 못한 스레드는 mutex 변수의 waiting queue에 들어가 대기하게 된다.
- pthread_mutex_trylock : OS에게 mutex 변수에 대한 lock을 얻을 수 있는지 체크한다.
- 이 때 mutex 변수를 block하지 않는다.
- 언제나 바로 반환된다.
- pthread_mutex_unlock : mutex 변수에 대한 lock을 해제한다.
- lock을 받은 스레드는 작업이 끝나면 무조건 lock을 해제해야 한다.
- lock이 해제되었으면 mutex 변수는 다시 waiting queue를 탐색하며 다음으로 요청한 스레드에게 lock을 준다. 동시에 다시 block된다.
- 만약 lock을 얻었으면 0을 반환하고, 아니면 에러 코드를 반환한다.
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mylock);
pthread_mutex_unlock(&mylock);
At-Most-Once execution
#include <pthread.h>
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;
- 초기화 함수같은 어떤 함수는 한번만 실행해야 되는 함수일 수 있다.
- 이것을 At-Most-Once execution이라고 한다.
- 프로그램에서 이런 특정 함수가 여러번 불릴 가능성이 있다.
- POSIX는 한번만 실행해야 되는 함수가 정확히 한번만 실행되도록 돕는 함수를 지원해준다.
- pthread_once : 어떤 함수가 프로세스 상에서 한번만 실행되도록 만든다.
- pthread_once_t *once_control : 어떤 변수에 대해서 한번만 실행하도록 하는 변수를 넣어준다. 이 변수는 아래 설정값으로 초기화되어야 한다.
- PTHREAD_ONCE_INIT : 한번만 실행되도록 하는 설정값이다.
- void (*init_routine)(void) : 실행할 함수이다. 매개변수가 없고 반환하는 것도 없는 함수를 실행한다.
At-Least-Once execution
- 어떤 함수는 적어도 한번 이상 실행해야 하는 함수일 수 있다.
- 초기화 함수는 At-Most-Once와 At-Least-Once 모두를 만족하는, Exactly once 함수가 되어야 한다.
Condition Variables
Busy Waiting
- Mutex 변수(lock)를 통한 스레드 동기화는 Critical Section을 만들어 동기화하는 방법이었다.
- Critical Section은 여러 스레드가 참조해도 한 스레드만 작업할 수 있도록 한다.
- 가장 기본적인 동기화 방법이다.
- Critical Section안에서 또 다른 동기화가 필요할 때가 있다.
- 어떤 스레드가 Critical Section안에 들어왔다.(lock을 얻음)
- 이 스레드의 작업 내용중 어떠한 조건을 만족해야 작업하는 내용이 있다.
- 예를 들어, 스레드가 수행하는 작업 중 while(x != y)라는 구문이 있을 때, x == y 상태라면 스레드가 기다리게 된다.
- 그러면 스레드가 x != y 가 될 때까지 계속 기다릴 것이며, 만약 다른 조치가 없다면 영원히 기다릴 것이다.
- 한 스레드가 영원히 기다리고 있으면 다른 스레드는 작업을 하지 못한다. 영원히 기다리지 않아도 조건이 만족될 때 까지 다른 스레드가 작업을 하지 못하는 건 비효율적이다.
- 이 상태를 Busy waiting이라고 부르며, 해결할 방법이 필요하다.
- Busy Waiting을 해결하는 방법은 다음과 같다.
- 스레드가 mutex에 대한 lock을 얻는다.
- 조건에 대한 테스트를 해야 한다.
- 만약 테스트가 참이면, mutex를 해제하고 반복에서 나간다.
- 만약 테스트가 거짓이면, mutex를 해제하고 스레드를 지연시킨다.
Condition Variable
- Condition Variable은 Busy waiting을 해결하기 위해 사용되는 변수이다.
- Condition Variable또한 Waiting Queue를 가지고 있으며, 이 안에 스레드들을 대기시킬 수 있다.
- Condition Variable이 Busy waiting을 해결하는 과정은 다음과 같다.
- 스레드가 Mutex를 통해 lock을 얻는다.
- 조건에 대한 테스트를 한다.
- 만약 조건을 만족하지 못했으면 wait함수를 통해 스레드를 지연시킨다. 이 wait함수는 뒤에서 후술
- wait함수는 Mutex의 lock을 자동으로 해제한다. Mutex의 lock이 해제되었으므로 접근하려는 다른 스레드가 lock을 얻는다.
- 만약 조건을 만족했으면 자신이 할 일을 하고 Mutex를 해제한다.
Creation / Initialization
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- Condition 변수는 선언하고 무조건 초기화를 해주어야 한다.
- pthread_cond_t
- condition lock을 나타내는 Condition 변수의 타입이다.
- 항상 사용하기 전에 초기화를 해주어야 한다.
- Condition 변수의 초기화는 두가지 방법이 존재한다.
- PTHREAD_COND_INITIALIZER : 간단하게 기본값으로 초기화한다. 주로 static값으로 선언된 변수를 초기화할때 사용한다.
- int pthread_cond_init : 변수를 이용하여 Condition값을 초기화한다. 속성과 같이 초기화하므로 내가 원하는 특정 값을 Condition 변수의 초기 값으로 설정할 수 있다는 장점이 있다.
- pthread_cond_t *restrict mutex : 초기화하려는 Condition 변수의 포인터 값이다.
- const pthread condattr_t *restrict attr : Condition 변수를 초기화 할 때 설정할 속성이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 초기화를 여러번 하는 경우는 정의되지 않았으므로, 한번만 할 수 있도록 해야한다.
- Condition 변수의 생성과 초기화는 Mutex 변수와 동일하다.
Destroy
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
- Condition 변수는 사용이 끝나면 동적 변수와 비슷하게 해제해줘야 한다.
- pthread_cond_destroy로 Condition 변수를 해제할 수 있다.
- pthread_cond_t *cond : 해제할 Condition 변수이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 스레드가 이미 해제된 Condition 변수를 참조하는 것을 정의하지 않았으므로, 어떤 오류가 생길 지 모른다. 그러므로 mutex 변수를 참조하는 스레드가 없는지 확인하고 해제해야 한다.
- 스레드가 Condition 변수를 사용중인데 해제하면, 어떤 오류가 생길 지 모른다. 그러므로 mutex 변수를 사용하는 스레드가 없는지 확인하고 해제해야 한다.
- Condition 변수의 삭제는 Mutex 변수와 동일하다.
Waiting
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- Mutex에 locking/unlocking이 있다면, Condition variable에는 Waiting과 Signaling이 있다.
- pthread_cond_wait : 이 함수를 호출한 스레드가 condition variable의 waiting queue로 들어간다. 동시에 mutex의 lock을 해제한다.
- pthread_cond_t *restrict cond : 앞에서 초기화한 condition variable을 넘겨준다.
- pthread_mutex_t *restrict mutex : 스레드가 지연됨과 동시에 unlock될 Mutex를 넘겨준다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- pthread_cond_timedwait : wait함수와 동일하다. 다른 점은 기다릴 시간을 정할 수 있다.
- pthread_cond_t *restrict cond : 앞에서 초기화한 condition variable을 넘겨준다.
- pthread_mutex_t *restrict mutex : 스레드가 지연됨과 동시에 unlock될 Mutex를 넘겨준다.
- const struct timespec *restrict abstime : 기다릴 시간을 정한다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 타이머가 만료되어 반환하면, ETIMEDOUT를 반환하기 때문에, 만료되어 반환되었음을 알 수 있다.
- 스레드가 Waiting하고 있는 중 Singal이 도착하면 2가지로 나뉜다.
- signal handler를 return하고 계속 Waiting한다.
- signal handler를 return하고 0을 반환하면서 스레드가 깨어난다.
- 그러므로 사용할 때 이러한 상황을 염두에 두어야 한다.
- 만약 두번째 매개변수(Mutex)를 잘못 지정할 경우에 대한 것을 고려하지 않았으며, Deadlock에 빠질 수 있으므로 주의해야한다.
Signaling
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
- pthread_cond_signal : 매개변수로 들어온 condition variable에 있는 스레드를 깨워서 지연시킨 Mutex에 되돌려준다.
- pthread_cond_broadcast : 매개변수로 들어온 condition variable에 있는 모든 스레드를 깨워서 지연시킨 Mutex에 되돌려준다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
Signal handling and threads
- 프로세스 안에 있는 모든 스레드들은 프로세스의 signal handler를 공유한다.
- 또한, 스레드들은 각자 signal mask를 가지고 있다.
- 다중 스레드 기반 프로세스에세 시그널이 오면, 어떤 스레드가 시그널을 받을지 애매해진다.
- 그러므로 다중 스레드 환경 프로세스가 시그널을 받는 방법은 3가지가 있다.
- Asynchronous : signal mask로 막지 않는 스레드에게 시그널을 전달한다.
- Synchronous : 여러 스레드 중 시그널을 발생시킨 스레드에게 시그널을 전달한다.
- Directed : 시그널의 대상인 스레드를 지정했으면, 그 스레드에게 시그널을 전달한다.
Directing a signal
#include <signal.h>
#include <pthread.h>
int pthread_kill(pthread_t thread, int sig);
- pthread_kill : 특정 스레드에게 시그널을 보낼 수 있다.
- pthread_t thread : 시그널을 보낼 스레드다.
- int sig : 보낼 시그널이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
Masking signals for threads
#include <pthread.h>
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
- 이 함수를 호출한 스레드의 signal mask를 변경한다.
- int how : signal mask를 어떻게 변경할 것인지 정한다.
- SIG_SETMASK : 기존을 무시하고 새로 설정한다.
- SIG_BLOCK : 기존은 그대로 두고 새로 추가한다.
- SIG_UNBLOCK : 기존은 그대로 두고 새로운 시그널들을 제거한다.
- const sigset_t *restrict set : 설정할 signal set이다.
- sigset_t *restrict oset : 기존에 있던 signal set을 받는 output parameter이다.
- 기존 signal mask정보를 oset에 저장한다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
Dedicating threads for signal handling
- 멀티 스레드 프로세스가 signal handling을 해야 하는 경우, 이것을 수행해주는 특정한 스레드를 만든다.
- 특정한 스레드는 시그널 관련 일들을 전담해서 수행한다.
- 스레드가 동작하는 방식은 다음과 같다.
- 일단 모든 시그널을 막는다.
- 시그널을 전담할 스레드를 하나 만든다.
- 스레드가 sigwait()를 호출해서 시그널을 기다린다.
- 만약 시그널이 들어오면, 다른 모든 스레드는 막아놨기 때문에 동작하지 않는다.
- 특정한 스레드는 sigwait()을 하고 있으므로, 내가 원하는 시그널이 들어오면 기다림이 해제되고 특정한 일을 수행한다.
Reader and Writer
Reader-writer problem
- Mutex lock의 목적은 어떤 특정한 코드 영역(Critical Section)을 여러 스레드가 동시에 엑세스하면 문제가 발생할 수 있기 때문에 하나의 스레드만 접근하도록 하기 위한 것이다.
- 스레드가 특정한 영역에 접근할 때는 2가지 행동 중 하나를 한다. 읽거나, 쓰는것이다.
- 쓰는 것은 여러 스레드가 하면 안되므로, 막아야 한다.
- 그러나 읽는 것은 여러 스레드가 동시에 해도 충돌 문제가 발생하지 않는다.
- 그러므로 스레드가 읽는 과정은 막지 않으면 성능을 올릴 수 있다.
- 이것을 read-write lock이라고 한다.
- read용으로 lock을 요청할 수도 있고, write용으로 lock을 요청할 수 있다.
- read용일때는 여러 스레드에 lock을 줄 수 있으며, write일때는 하나의 스레드에만 lock을 준다.
- read와 write에 lock을 주는 상황에서, 우선순위가 중요하다.
- Strong reader synchronization : Read 요청을 하는 스레드에게 먼저 lock을 주는 방식이다. Read 요청이 없을 때 Write 요청 스레드에게 lock을 준다.
- Strong writer synchronization : Write 요청을 하는 스레드에게 먼저 lock을 주는 방식이다. Write 요청이 없을 때 Read 요청 스레드에게 lock을 준다.
- 또한 write 요청이 하나라도 있으면, mutex lock과 똑같이 작동해야한다. read하는 중 write가 되면 문제가 생길 수 있기 때문이다.
Creation / Initialization
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr)
- Read-Write 변수는 선언하고 무조건 초기화를 해주어야 한다.
- pthread_rwlock_t
- Read-Write lock을 나타내는 Condition 변수의 타입이다.
- 항상 사용하기 전에 초기화를 해주어야 한다.
- int pthread_cond_init : 변수를 이용하여 Read-Write 변수 값을 초기화한다. 속성과 같이 초기화하므로 내가 원하는 특정 값을 Condition 변수의 초기 값으로 설정할 수 있다는 장점이 있다.
- pthread_cond_t *restrict rwlock : 초기화하려는 Read-Write 변수의 포인터 값이다.
- const pthread rwlockattr_t *restrict attr : Read-Write 변수를 초기화 할 때 설정할 속성이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 초기화를 여러번 하는 경우는 정의되지 않았으므로, 한번만 할 수 있도록 해야한다.
- Read-Write 변수의 생성과 초기화는 Condition, Mutex 변수와 동일하다.
Destroy
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwloc
- Read-Write 변수는 사용이 끝나면 동적 변수와 비슷하게 해제해줘야 한다.
- pthread_rwlock_destroy로 Read-Write 변수를 해제할 수 있다.
- pthread_rwlock_t *rwloc : 해제할 Read-Write 변수이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- 스레드가 이미 해제된 Read-Write 변수를 참조하는 것을 정의하지 않았으므로, 어떤 오류가 생길 지 모른다. 그러므로 Read-Write 변수를 참조하는 스레드가 없는지 확인하고 해제해야 한다.
- 스레드가 Read-Write 변수를 사용중인데 해제하면, 어떤 오류가 생길 지 모른다. 그러므로 mutex 변수를 사용하는 스레드가 없는지 확인하고 해제해야 한다.
- Read-Write 변수의 삭제는 Condition, Mutex 변수와 동일하다.
Locking/Unlocking
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- Read-Write 변수는 Mutex 변수와 기능이 비슷하지만, 경우가 많으므로 함수도 많아진다.
- 함수의 종류와 기능은 다음과 같다.
- rdlock / tryrdlock : 읽기 전용의 lock을 요청하는 함수이다.
- wrlock/ trywrlock : 쓰기 전용의 lock을 요청하는 함수이다.
- unlock : lock을 해제한다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- tryrdlock과 trywrlock은 lock을 받지 못해도 바로 반환이 된다. 이 때 반환값은 EBUSY이며, lock이 다른 스레드에게 할당되어 았다는 뜻이다.
- wrlock을 얻었을 때 rdlock을 요청하는 경우를 정의하지 않았으므로, 아마 deadlock이 걸릴 것이다. 그러므로 사용할 때 조심해야 한다. 반대도 같다.
Strerror_r implementation
- strerror는 에러 메세지를 표시하는 함수이다.
- 문제는 이 함수가 thread-safe하지 않다는 것이다.
- 그러므로 mutex lock을 사용해서 thread-safe하게 사용해야 한다.
- 마찬가지로 perror또한 thread-safe하지 않으므로 변형해서 사용해야 한다.
- 사용 방법은 다음과 같다.
Deadlock
- 스레드를 사용하는 건 성능적으로 굉장한 이득을 얻을 수 있다.
- 그러나 큰 힘에는 큰 책임이 따른다고 한 것처럼 스레드 사용은 고려해야 할 것들이 많다.
- 위에 나온 여러가지 상황을 고려하지 않으면 Deadlock에 걸리게 되고 스레드를 안쓰니만 못하게 될 것이다.
- 대표적인 Deadlock을 유발하는 상황들이다.
- 스레드가 lock을 가지고 있으면서 lock을 요청함 : 자신이 끝나기를 기다리므로 끝내지 못하게 됨. 설정에 따라 에러를 반환할 수 있지만 아닐수도 있음
- lock을 가지고 있는 스레드가 에러를 만나게 되는 경우 : 스레드가 에러를 만나게 되면 그냥 끝나게(혹은 return) 되는 경우가 많음. 이렇게 되면 unlock이 되지 않고 끝나므로 다른 스레드가 작업을 할 수 없게 됨
- 스레드간의 우선순위 : 스레드의 우선순위가 밀려 lock을 오래 얻지 못하면 사실상 작업하지 못하게 되는 것이므로, 우선순위를 잘 설정해야 함