-
시스템 프로그래밍 실습 13주차 : Synchronization 1System Programming/Ubuntu Linux 2021. 11. 21. 16:41728x90
시스템 프로그래밍 실습 13주차 : Synchronization 1
먼저, 11주차 복습!
https://asidefine.tistory.com/123
- Mutex API
: mutex 는 여러 개의 쓰레드가 공유하는 데이터를 보호하기 위해서 사용되는 도구로써, 보호하고자 하는 데이터를 다루는 코드영역을 단지 '한번에 하나의 쓰레드만' 실행가능 하도록 하는 방법으로 공유되는 데이터를 보호한다.
이러한 코드영역(하나의 쓰레드만 점유가능한)을 critical section 이라고 하며, mutex 관련 API 를 이용해서 관리할수 있다.
- Conditional variable API
: conditonal variable은 공유 데이터에 대한 특정 조건에 따라 Thread의 실행을 중지하거나 다시 실행하는 역할을 하는 동기화 장치다. 조건 변수에 대한 기본 연산은 1) 조건 변수에 시그널을 보낸다(특정 조건이 만족된 경우), 2) 조건 변수를 기다린다 (다른 쓰레드가 해당 조건 변수에 시그널을 보낼 때까지 쓰레드의 실행이 중지됨)
=> 조건 변수는 한 쓰레드가 조건 변수를 기다릴 준비를 하는 동안 (실제로 대기하기 전에) 다른 쓰레드가 해당 조건 변수에 시그널을 보내는 것과 같은 경쟁 상태를 방지하기 위해 항상 뮤텍스와 함께 사용되어야 한다.[목차]
- Mutex
- pthread mutex 초기화 방법
- Mutex 함수들
- 예제
- Condition Variable
- pthread CV 초기화 방법
- CV 함수들
- 예제
- Thread Safety
1. Mutex
: mutex 는 여러 개의 쓰레드가 공유하는 데이터를 보호하기 위해서 사용되는 도구로써, 보호하고자 하는 데이터를 다루는 코드영역을 단지 '한번에 하나의 쓰레드만' 실행가능 하도록 하는 방법으로 공유되는 데이터를 보호한다.
이러한 코드영역(하나의 쓰레드만 점유가능한)을 critical section 이라고 하며, mutex 관련 API 를 이용해서 관리할수 있다.1) pthread mutex 초기화 방법
(1) 정적 초기화
#include <pthread.h> pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
(2) 동적 초기화
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
파라미터
mutex : 초기화 받을 mutex 객체
attr : 초기화 할 때 뮤텍스의 특징을 정의할 수 있는 속성 (기본으로 사용하려면 NULL)반환값
성공 시 : 0 반환
실패 시 : 에러 넘버2) mutex 함수들
(1) pthread_mutex_init - 뮤텍스 객체를 생성(초기화)한다
(2) pthread_mutex_destroy - 뮤텍스가 할당받은 리소스를 해제시킨다
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex);
파라미터
mutex : free 시킬 뮤텍스
반환
성공 시 : 0 반환
실패 시 : 에러 넘버(3) pthread_mutex_lock - 기본적으로 뮤텍스 객체를 lock 시킨다. 이미 뮤텍스가 lock인 상태이면 block 상태로 들어가서 mutex를 사용할 수 있을 때까지 대기한다. / critical section 시작
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex);
파라미터
mutex : lock 시킬 뮤텍스
반환
성공 시 : 0
실패 시 : 에러 넘버(4) pthread_mutex_unlock - 현재 쓰레드가 뮤텍스를 잡고 있으면 unlock 시킨다. 자신이 갖고 있는 뮤텍스가 아닌 것을 unlock 시키면 undefined / critical section 종료
#include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex);
파라미터
mutex : unlock 시킬 뮤텍스
반환값
성공 시 : 0
실패 시 : 에러 넘버(5) pthread_mutex_trylock
3) 예제
예제 1)
#include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <pthread.h> #include <string.h> #include <errno.h> pthread_mutex_t mutex; void *routine(void *thread_number) { pthread_mutex_lock(&mutex); printf("Thread[%d]: mutex lock\n", *(int *)thread_number); for (int i = 0; i < 5; i++) { usleep(1000 * 1000); printf("Thread[%d]: Wating %d Seconds\n", *(int *)thread_number, i); } printf("Thread end\n"); pthread_mutex_unlock(&mutex); printf("Thread[%d]: mutex unlock\n", *(int *)thread_number); return (thread_number); } int main(void) { pthread_t thread1; pthread_t thread2; int number1; int number2; void *ret; number1 = 1; number2 = 2; printf("Pthread create\n"); pthread_create(&thread1, NULL, routine, (void *)&number1); pthread_create(&thread2, NULL, routine, (void *)&number2); printf("Mutex init\n"); pthread_mutex_init(&mutex, NULL); printf("Wating for threads\n"); pthread_join(thread1, &ret); // thread1을 대기한다. routine의 값을 ret에 넣어준다. pthread_join(thread2, &ret); // thread1을 대기한다. routine의 값을 ret에 넣어준다. printf("Mutex destroy\n"); pthread_mutex_destroy(&mutex); printf("main end\n"); return 0; }
예제 2)
#include <stdio.h> #include <unistd.h> #include <pthread.h> int ncount; // 쓰레드간 공유되는 자원 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화 void* do_loop(void *data) { int i; for (i = 0; i < 10; i++) { pthread_mutex_lock(&mutex); // 잠금을 생성한다. printf("loop1 : %d\n", ncount); ncount ++; if(i == 10) break; pthread_mutex_unlock(&mutex); // 잠금을 해제한다. sleep(1); } return 0; } void* do_loop2(void *data) { int i; // 잠금을 얻으려고 하지만 do_loop 에서 이미 잠금을 // 얻었음으로 잠금이 해제될때까지 기다린다. for (i = 0; i < 10; i++) { pthread_mutex_lock(&mutex); // 잠금을 생성한다. printf("loop2 : %d\n", ncount); ncount ++; pthread_mutex_unlock(&mutex); // 잠금을 해제한다. sleep(2); } return 0; } int main() { int thr_id; pthread_t p_thread[2]; int status; int a = 1; ncount = 0; thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a); sleep(1); thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a); pthread_join(p_thread[0], (void *) &status); pthread_join(p_thread[1], (void *) &status); // 부모프레스가 얘들 끝날 때까지 join에서 기다리는듯. status = pthread_mutex_destroy(&mutex); //뮤텍스 파괴!!! printf("code = %d", status); printf("programing is end"); return 0; }
=> do_loop 쓰레드가 ncount 증가 작업을 모두 마칠 때까지 do_loop2 쓰레드는 해당 영역에서 블럭됨을 알 수 있다
예제 3)
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 뮤텍스 객체 선언 pthread_mutex_t mutex_lock; int g_count = 0; // 쓰레드 공유자원. void *t_function(void *data) { int i; char* thread_name = (char*)data; pthread_mutex_lock(&mutex_lock); // critical section g_count = 0; // 쓰레드마다 0부터 시작. for (i = 0; i < 3; i++) { printf("%s COUNT %d\n", thread_name, g_count); g_count++; // 쓰레드 공유자원 sleep(1); } pthread_mutex_unlock(&mutex_lock); } int main() { pthread_t p_thread1, p_thread2; int status; // 뮤텍스 객체 초기화, 기본 특성으로 초기화 했음 pthread_mutex_init(&mutex_lock, NULL); pthread_create(&p_thread1, NULL, t_function, (void *)"Thread1"); pthread_create(&p_thread2, NULL, t_function, (void *)"Thread2"); pthread_join(p_thread1, (void *)&status); pthread_join(p_thread2, (void *)&status); }
=> 이제 pthread_mutex_lock() 과 pthread_mutex_unlock() 사이의 critical section 은 한번에 하나의 쓰레드만 수행할수 있고, 먼저 이 critical section에 진입한 쓰레드가 종료할때까지 다른 쓰레드는 대기상태에 있다가, 앞선 쓰레드가 critical section 을 끝내고 빠져나오면 진입하게 된다
Thread2 COUNT 0
Thread2 COUNT 1
Thread2 COUNT 2
Thread1 COUNT 0
Thread1 COUNT 1
Thread1 COUNT 2
예제 4)https://reakwon.tistory.com/98
https://bitsoul.tistory.com/172?category=683199
https://velog.io/@t1won/C-mutex
https://www.crocus.co.kr/528?category=204622
https://www.crocus.co.kr/486?category=204622
2. Condition Variable
=> 뮤텍스는 lock, unlock, destroy만으로 구성되어 있고, 리소스에 대한 베타적 접근이라는 간단한 동기화를 제공해준다. 하지만 여기에는 한계가 존재한다!
: conditonal variable은 공유 데이터에 대한 특정 조건에 따라 Thread의 실행을 중지하거나 다시 실행하는 역할을 하는 동기화 장치다. 조건 변수에 대한 기본 연산은 1) 조건 변수에 시그널을 보낸다(특정 조건이 만족된 경우), 2) 조건 변수를 기다린다 (다른 쓰레드가 해당 조건 변수에 시그널을 보낼 때까지 쓰레드의 실행이 중지됨)
=> 조건 변수는 한 쓰레드가 조건 변수를 기다릴 준비를 하는 동안 (실제로 대기하기 전에) 다른 쓰레드가 해당 조건 변수에 시그널을 보내는 것과 같은 경쟁 상태를 방지하기 위해 항상 뮤텍스와 함께 사용되어야 한다.1) pthread CV 초기화 방법
(1) 정적 초기화
#include <pthread.h> pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
(2) 동적 초기화
#include <pthread.h> pthread_cond_t cond; pthread_cond_init(&cond, (pthread_condattr_t *)NULL);
2) CV 함수들
(1) pthread_cond_init - condition 객체 초기화
#include <pthread.h> int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
(2) pthread_cond_destroy - 조건 변수를 삭제하고, 조건 변수에 할당된 자원을 해제한다
#include <pthread.h> int pthread_cond_destroy(pthread_cond_t *cond);
(3) pthread_cond_wait - 조건 변수가 신호를 받을 때까지 기다리는 역할을 한다
#include <pthread.h> int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
(4) pthread_cond_signal - cond 조건 변수에 신호를 보내 이 cond조건 변수를 기다리고 있는 스레드 중의 하나를 다시 시작시키게 된다 / 조건 변수 cond (을)를 기다리고 있는 1 개의 thread의 블록을 해제
#include <pthread.h> int pthread_cond_signal(pthread_cond_t *cond);
(5) pthread_cond_broadcast - cond 조건 변수를 기다리고 있는 모든 스레드를 다시 시작시킨다 / 조건 변수 cond (을)를 기다리고 있는 모든 thread의 블록을 해제
#include <pthread.h> int pthread_cond_broadcast(pthread_cond_t *cond);
3) 예제
예제 1)
#include <stdio.h> #include <pthread.h> // pp ;: pingpong pthread_mutex_t ppmutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ppcond = PTHREAD_COND_INITIALIZER; int flag = 0; void *ping(void *argumentPointer) { int i = 0; printf("ping 먼저 \n"); while(i <= 100) { pthread_mutex_lock(&ppmutex); printf("ping :: %d\n",i); i++; flag = 1; pthread_cond_signal(&ppcond); pthread_cond_wait(&ppcond,&ppmutex); pthread_mutex_unlock(&ppmutex); } return NULL; } void *pong(void *argumentPointer) { int i = 0; printf("pong 먼저 \n"); while(i <= 100) { pthread_mutex_lock(&ppmutex); while(flag == 0) pthread_cond_wait(&ppcond, &ppmutex); flag = 0; printf("pong :: %d\n",i); i++; pthread_cond_signal(&ppcond); pthread_mutex_unlock(&ppmutex); } return NULL; } int main() { pthread_t threadID1, threadID2; // Create < 뮤텍스 이용 > pthread_create(&threadID1, NULL, ping, NULL); pthread_create(&threadID2, NULL, pong, NULL); // Join < 뮤텍스 이용 > pthread_join(threadID1, NULL); pthread_join(threadID2, NULL); printf("Ping Pong finish !! \n"); return 0; }
=>
예제 2)
https://reakwon.tistory.com/99?category=300674
3. Thread Safety
Thread가 안전하지 않은 함수들에 대해 네 가지 경우로 나눠 보았다!
Class 1: 공유 변수들에 대해서 보호하는 것을 실패했을 때 (Failing to protect shared variables)'
Class 2: 여러 함수 호출에 걸쳐 고정된 상태에 의존?? (Relying on persistent state across multiple function invocations)
Class 3 : 정적 변수에 포인터를 return했을 때 (Returning a ptr to a static variable)
Class 4 : thread unsafe한 함수들을 불렀을 때 (Calling thread-unsafe functions)
Class 1: 공유 변수들에 대해서 보호하는 것을 실패했을 때 (Failing to protect shared variables)
=> 해결방안 : mutex 연산들을 사용한다!
Class 2 : 여러 함수 호출에 걸쳐 고정된 상태에 의존?? (Relying on persistent state across multiple function invocations)
Class 3 : 정적 변수에 포인터를 return했을 때 (Returning a ptr to a static variable)
=> 해결방안 :
1)
2)
Class 4 : thread unsafe한 함수들을 불렀을 때 (Calling thread-unsafe functions)
=> 해결 방안 : 안전한 함수만 부르자 ~
728x90'System Programming > Ubuntu Linux' 카테고리의 다른 글
시스템 프로그래밍 실습 14주차 : Synchronization 2 (0) 2021.11.24 시스템 프로그래밍 실습 12주차 : Concurrent Programming (0) 2021.11.15 시스템 프로그래밍 실습 11주차 : Pthreads (0) 2021.11.08 시스템 프로그래밍 실습 10주차 : Sockets (0) 2021.11.01 Shell 만들기 참고할 것 (0) 2021.10.28