Threads Synchornization
Multi Thread에서 전역 변수, 힙 영역, malloc 등은 Data Sharing을 한다.
다음 코드는 입력한 초만큼 전역 변수 cnt가 증가하는 예제이다.
volatile: 컴파일러 최적화 방지. cnt 변수를 레지스터가 아닌 메모리에서 직접 읽고 쓰게 만듦
volatile int cnt;
void* do_loop (void *loop)
{
while(1)
cnt++;
return NULL;
}
int main(int argc, char *argv[]){
int status, sec;
pthread_t thread_id;
cnt = 0;
sec = atoi(argv[1]);
if (pthread_create(&thread_id, NULL, do_loop, NULL) !=0){
perror("pthread create");
exit(1);
}
sleep(sec);
pthread_cancel(thread_id);
printf("counter = %d\n", cnt);
}
그 이유로 Race Condition이 발생할 수 있다.
아래는 Race Condition의 예제이다.
volatile int cnt;
void* do_loop1 (void *data)
{
int* loop_p = (int *)data;
for (int i = 0; i < *loop_p; i++)
cnt++;
return NULL;
}
void* do_loop2 (void *data)
{
int* loop_p = (int *)data;
for (int i = 0; i < *loop_p; i++)
cnt--;
}
int main(int argc, char *argv[]){
pthread_t thread_id[2];
int loop_n = atoi(argv[1]);
if (pthread_create(&thread_id[0], NULL, do_loop1, &loop_n) !=0){
perror("pthread1 create");
exit(1);
}
if (pthread_create(&thread_id[1], NULL, do_loop2, &loop_n) !=0){
perror("pthread2 create");
exit(1);
}
pthread_join(thread_id[0],NULL);
pthread_join(thread_id[1],NULL);
printf("counter = %d\n", cnt);
}
결과
- 각 스레드의 반복 횟수가 많아질수록 경쟁 상태가 발생할 확률이 높아진다.
- Race Condition: 실행할 때마다 결과 값이 달라지는 현상.
Locks: Basic Idea
Critical Section이 단일 Atomic Instruction처럼 동작하도록 Mutual Exclusion을 보장한다.
즉, 다른 스레드가 접근하지 못하게 막아준다.
lock_t mutex; // available, acquired 둘중 하나의 상태를 저장
lock(&mutex);
balance = balance + 1;
unlock(&mutex);
lock()
- Lock을 얻으려 시도하며, 다른 스레드가 hold 중이 아니라면 lock을 획득한다.
- Lock을 건 스레드만 Critical Section에 접근 가능하다.
Mutex
pthread 라이브러리의 자료구조로, Thread 동기화를 구현한다.
Static Initialization
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALZER; // 정적 초기화
Dynamic Initialization
pthread_mutex_t mutex; // 이걸 인자로 &mutex 넣으면 된다.
int pthread_mutex_init(
pthread_mutex_t *mutex, // mutex 변수의 포인터
pthread_mutexattr_t *attr) // NULL, mutex 속성
) // 반환 0 또는 에러코드
Locking Mutex
- pthread_mutex_lock()
- 성공 시 lock을 획득, 실패 시 대기.
- 선발대만 lock을 얻고, 나머지는 sleep 상태가 된다.
- pthread_mutex_trylock()
- 성공 시 lock을 획득, 실패 시 바로 반환.
- 후발대는 대기하지 않고 즉시 리턴.
int pthread_mutex_lock(pthread_mutex_t *mutex) // 성공하면 잡음. 성공 못하면 기다림
int pthread_mutex_trylock(pthread_mutex_t *mutex) // 성공하면 잡음. 성공 못하면 바로 리턴(알려줌)
Unlocking Mutex
- pthread_mutex_unlock()
- Mutex를 release.
- Block 상태의 스레드가 있으면 Scheduling Policy에 따라 mutex를 얻을 스레드 결정.
int pthread_mutex_unlock(pthread_mutex_t *mutex)
Race Condition 방지: Mutex 사용 예제
- pthread_mutex_destroy(): 프로그램 종료 시 mutex 자원 해제.
volatile int cnt;
// int cnt; // volatile을 안한다면???
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* do_loop1 (void *data)
{
int* loop_p = (int *)data;
pthread_mutex_lock(&mutex);
for (int i = 0; i < *loop_p; i++)
cnt++;
pthread_mutex_unlock(&mutex);
return NULL;
}
void* do_loop2 (void *data)
{
int* loop_p = (int *)data;
pthread_mutex_lock(&mutex);
for (int i = 0; i < *loop_p; i++)
cnt--;
pthread_mutex_unlock(&mutex);
}
int main(int argc, char *argv[]){
pthread_t thread_id[2];
int loop_n = atoi(argv[1]);
if (pthread_create(&thread_id[0], NULL, do_loop1, &loop_n) !=0){
perror("pthread1 create");
exit(1);
}
if (pthread_create(&thread_id[1], NULL, do_loop2, &loop_n) !=0){
perror("pthread2 create");
exit(1);
}
pthread_join(thread_id[0],NULL);
pthread_join(thread_id[1],NULL);
printf("counter = %d\n", cnt);
pthread_mutex_destroy(&mutex);
}
반응형
'CS 지식 > 시스템 프로그래밍' 카테고리의 다른 글
[C언어] pthread 라이브러리를 이용해서 스레드에게 signal 보내기 (0) | 2025.01.28 |
---|---|
[C언어] Deadlock(교착상태) 원인과 해결법 (0) | 2025.01.28 |
[C언어] 스레드의 개념과 pthread 라이브러리 (0) | 2025.01.28 |
[C언어] 프로세스 간 통신 (IPC) (0) | 2024.10.18 |
Assembly(어쌤블리어)의 기초 (0) | 2024.10.18 |
댓글