Mutex Gotchas
So pthread_mutex_lock stops the other threads when they read the same variable?
int a; pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER, m2 = PTHREAD_MUTEX_INITIALIZER; // later // Thread 1 pthread_mutex_lock(&m1); a++; pthread_mutex_unlock(&m1); // Thread 2 pthread_mutex_lock(&m2); a++; pthread_mutex_unlock(&m2);
위와 같은 경우는 서로 다른 mutex를 사용하므로 여전히 race condition에 빠져있습니다.
Can I create mutex before fork-ing?
물론 가능하지만 child와 parent process는 가상 메모리를 공유하지 않고 각각은 서로 다른 mutex를 독립적으로 갖습니다.
(고급 옵션으로 공유된 메모리를 사용하면 child와 parent가 mutex를 공유할 수 있습니다. 적절한 옵션으로 공유 메모리 segment를 사용하면 child와 parent의 mutex를 공통으로 사용할 수 있습니다.)
If one thread locks a mutex can another thread unlock it?
Can I use two or more mutex locks?
만약 하나의 lock만 존재한다면 불필요하게 두 개의 스레드가 하나의 lock을 가지고 경쟁할 수 있습니다. 예를 들어 두 스레드가 다른 counter를 update하는데, 동일한 lock으로 사용할 필요가 없습니다.
그러나 단순히 많은 lock을 만드는것으로는 불충분합니다. CS에 대해서 추론하는 것이 중요합니다. 하나의 스레드가 두 자료구조가 업데이트 되고 있는 중에 읽어 불안정한 상태가 되도록 않도록 하는 것이 중요합니다.
Is there any overhead in calling lock and unlock?
pthread_mutex_lock과 __unlock을 호출하는 것은 작은 overhead가 존재합니다. 그러나 이 대가는 정확하게 작동하는 프로그램을 작성하기 위해 지불해야 하는 대가입니다!
Simplest complete example?
#include <stdio.h> #include <pthread.h> // Compile with -pthread // Create a mutex this ready to be locked! pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int sum = 0; void *countgold(void *param) { int i; // Same thread that locks the mutex must unlock it // Critical section is just 'sum += 1' // However locking and unlocking a million times // has significant overhead in this simple answer pthread_mutex_lock(&m); // Other threads that call lock will have to wait until we call unlock for (i = 0; i < 10000000; i++) { sum += 1; } pthread_mutex_unlock(&m); return NULL; } int main() { pthread_t tid1, tid2; pthread_create(&tid1, NULL, countgold, NULL); pthread_create(&tid2, NULL, countgold, NULL); //Wait for both threads to finish: pthread_join(tid1, NULL); pthread_join(tid2, NULL); printf("ARRRRG sum is %d\n", sum); return 0; }
위 코드에서 스레드는 counting house에 진입하기 전에 lock을 얻습니다. CS는 정확히 sum +=1인 부분이므로
for (i = 0; i < 10000000; i++) { pthread_mutex_lock(&m); sum += 1; pthread_mutex_unlock(&m); } return NULL; }
위와 같이 이 부분에만 lock을 얻을 수 있지만 이렇게 한다면 mutex의 lock과 unlock을 백만 번 반복하므로 속도가 더 느려집니다. 적어도 incrementing variable보다는 훨씬 overhead가 크기 때문입니다. (실제 이 예에서는 스레드도 필요 없습니다- 단지 2번 반복하면 끝나는 일이기 때문이죠)
더 빠른 multi-thread example은 아래와 같이 지역 변수에서 백만번을 더하고 나중에 lock을 건 후에 이 지역변수값을 sum에 더하는 것입니다.
int local = 0; for (i = 0; i < 10000000; i++) { local += 1; } pthread_mutex_lock(&m); sum += local; pthread_mutex_unlock(&m); return NULL; }
What happens if I forgot to unlock?
while (not_stop) { // stdin may not be thread safe pthread_mutex_lock(&m); char *line = getline(...); if (rand() % 2) { /* randomly skip lines */ continue; } pthread_mutex_unlock(&m); process_line(line); }
When can I destroy the mutex?
mutex가 unlock되엇을 때만 destroy가 가능합니다.
Can I copy a pthread_mutex_t to a new memory location?
What would a simple implementation of a mutex look like?
// Version 1 (Incorrect!) void lock(mutex_t *m) { while(m->locked) { /*Locked? Nevermind - just loop and check again!*/ } m->locked = 1; } void unlock(mutex_t *m) { m->locked = 0; }
unlock function은 단순히 mutex를 unlock해 반환해줍니다. lock function은 일단 lock이 locked된 상태인제 확인한 후에 이미 locked되어 있는 상태라면 다른 스레드가 이 mutex를 unlock할 때까지 계속 체크합니다.
Version 1은 busy-waiting(불필요하게 CPU resource를 사용)을 사용하고 있고, 더욱 심각한 문제로는 race condition을 가지고 있다는 것입니다.
만약 두개의 스레드가 lock을 동시에 호출한다면 두 스레드 모두 m->locked를 0으로 읽을 수 있습니다. 그런 경우 두 스레드 모두 독점적인 접근이 가능하다고 믿고 두 스레드 모두 진행되게 됩니다.
loop안에서 pthread_yield()를 호출해서 CPU overhead를 줄이려는 시도를 할 수 있습니다.
pthread_yield는 OS에게 이 스레드가 잠시동안 CPU를 사용하지 않도록 제안하는 것입니다. 그렇게 되면 CPU는 동작하길 기다리는 다른 스레드에게 할당됩니다. 하지만 이것이 race-condition을 고쳐주지는 않습니다. race condition을 막을 더 좋은 구현이 필요합니다.
'Angrave System Programming > Synchronization' 카테고리의 다른 글
Synchronization: The Critical Section Problem(1) (0) | 2019.01.15 |
---|---|
Synchronization: Working with Mutexes And Semaphores(2) (0) | 2019.01.15 |
Synchronization: Working with Mutexes And Semaphores(1) (0) | 2019.01.15 |
Synchronization: Counting Semaphores (0) | 2019.01.15 |
Synchronization : Mutex Locks(1) (0) | 2019.01.14 |