일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 해시
- CSS 기초
- 자바스크립트
- 오블완
- select
- 우선순위큐
- 티스토리챌린지
- 자바문자열구분
- Entity
- Node
- html기초
- K-MOOC 단국대학교 홍보단
- 반정규화
- StringTokenizer
- 블록체인강의
- national instruments
- stringreader
- html 태그
- K-MOOC
- 2차원배열정렬
- 블록체인
- 자바입력받기
- 단국대학교 k-mooc
- 시스템프로그래밍
- 블록체인 강의
- 혁신의기술2:신뢰의미래 블록체인을 만나다
- attribute
- biginteger사용법
- StringBuilder
- 컴파일시스템
- Today
- Total
열정 실천
"병행성"을 위한 mutex_lock, 그리고 semaphore 본문
Concurrency - 병행성을 만족시키기 위한 두 가지 작업
여러 프로세스가 번갈아가면서 실행되고, 하나의 프로세스 안에 또 여러 스레드가 번갈아 실행되면서 문제 없이 동시에 잘 실행 (병행성) 되기 위해서는 두 가지가 만족되어야 하는데, 바로 동기화와 상호배제이다.
동기화 (Synchronization) 는 여러 스레드사이에 순서 조건이 있을 때 순서를 지킬 수 있도록 하는 것이고(스레드 1은 스레드 2보다 항상 먼저 실행되어야 한다던가..),
상호배제 (mutual exclusion) 는 임계영영에 하나의 스레드만 진입할 수 있도록 하는 것이다.
상호배제가 뭐고 임계영역은 또 뭐냐고!?
🙄 자! 전역 변수 count가 있다고 치자.
같은 코드와 변수를 공유하는 두 개의 스레드는 count ++; 을 100번 실행하는 코드를 수행하려고 한다.
이 두 스레드는 스케쥴링에 의해 번갈아 실행된다.
사이좋게 번갈아 가면서 1씩 증가시켜 답이 200이 나오면 좋겠지만,, 실제로는 그보다 작은 값이 나온다.
🤔 왜냐?!
실제로 count를 1증가시키는 코드는
1. 메모리에서 count 값을 가져온다음 (load)
2. 값을 1증가시키고 (add)
3. 다시 메모리에 저장한다. (save)
이렇게 3번의 작업을 수행해야 한다.
1번 스레드가 5라는 count 값을 가져온 다음 2번 스레드에게 cpu를 뺏겨 기다리고 있다.
2번 스레드는 같은 5라는 count 값을 가져와 1을 증가시키고 6을 메모리에 저장하는 데까지 성공했다.
그리고 다시 1번 스레드의 차례가 돌아왔고, 1번 스레드는 아까 가져왔던 5라는 값에 1을 증가 시킨 후 6을 저장했다.
😮 띠용!
두 번의 코드가 실행되었기 때문에 5에서 7이 되어있어야 할 count 값이 6에 머물러있다.
이는 같은 공유 자원을 스레드가 한 번에 (원자적으로) 실행하지 못했기 때문에 발생한 문제이다.
🤗
이렇게 두 개 이상의 스레드가 자원을 공유하여 매번 결과가 달라지는 것을 경쟁상태 (Race Condition) ,
공유 자원인 count 값이 업데이트되는 코드부분을 임계영역 (CS : Critical Section) ,
이러한 문제를 방지하기 위해 하나의 프로세스만 임계 영역에 진입하게 하는 것을 상호배제 (Mutual Exclusion) 이라고 한다.
상호배제를 위한 방법인 spin_lock과 mutex_lock에 대해 알아보자!
스핀락 spin_lock
Test & Set 코드
boolean test_and_set (boolean *target){
boolean rv = *target;
*target = true;
return rv;
}
boolean lock false;
do {
while(test_and_set(&lock)); /*do nothing*/
/*critical section*/
lock = false;
/*remainder section*/
}while(true);
lock 이 false인 경우에만 while문을 빠져나오고 true인 경우에는 무한 루프를 돌게 된다.
lock의 초기값은 false이기 때문에 맨 처음으로 들어가는 프로세스는 test_and_set 함수에서 lock 값을 true로 설정하고 return값은 false로 while문에서 바로 빠져나와 임계영역에 들어간다. 그 뒤로 들어온 프로세스들은 첫 번째 프로세스가 임계영역에서 나오기 전까지 test_and_set 값이 계속 true로 무한루프를 돌게 된다. 임계영역이 끝난 첫 번째 프로세스는 다시 lock 을 false로 설정한다. 이 바로 다음으로 실행되는 프로세스는 첫 번째 프로세스와 동일하게 lock 값을 false에서 true로 바꾸고 임계영역에 진입한다.
이렇게 임계영역에 들어가기 전까지 계속 값을 확인하며 무한루프를 돌아 스핀락이라고 부른다.
Mutex_lock
API
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Example
int pthread_mutex_lock(&lock);
//critical section//
int pthread_mutex_unlock(&lock);
mutex_lock을 적용한 코드 예시!!
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
pthread_mutex_t mutex;
int counter = 0;
void *critical_section(void *arg){
for(int i = 0; i<1000; i++){
pthread_mutex_lock(&mutex);
counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(){
pthread_t threads[2];
struct timeval start, end;
//mutex 초기화
pthread_mutex_init(&mutex, NULL);
//시간 측정 시작
gettimeofday(&start, NULL);
//스레드 생성
for(int i=0; i<2; i++){
pthread_create(&threads[i], NULL, critical_section, NULL);
}
//스레드 종료 대기
for(int i=0; i<2; i++){
pthread_join(threads[i], NULL);
}
//시간 측정 종료
gettimeofday(&end, NULL);
//결과 출력
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - start.tv_usec;
printf("Mutex lock completes in %ld microseconds\n", micros);
printf("Final counter value: %d\n", counter);
//mutex 제거
pthread_mutex_destroy(&mutex);
return 0;
}
mutex_lock 과 spin_lock 실행시간비교
병행성을 위한 또다른 이슈 :: 동기화
Synchronization - 동기화
동기화는 순서를 지켜서 실행하는 것!
스레드 2보다 스레드 1이 먼저 실행되어야할 때 스레드 1이 끝나기 전까지 스레드 2는 wait 상태에서 기다리는 것이다.
위의 mutex_lock을 이용한 동기화 방법
wait과 signal함수의 두 번째 인자로 mutex_lock 변수가 들어간다.
API
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); //신호올 때까지 wait
int pthread_cond_signal(pthread_cond_t *cond, pthread_mutex_t *mutex); //신호 보냄
Thread1
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
-- 스레드 1이 할 일... --
Thread2
-- 스레드 2가 할 일... --
pthread_cond_signal(pthread_cond_t *cond, pthread_mutex_t *mutex);
또,, 상호배제와 동기화를 만족시키기 위한 방법으로 세마포어가 있는데,,,,!!
Semaphore - 세마포어
"상호 배제"를 위한 세마포어의 초기값 : 1
wait(mut);
--임계영역--
signal(mut);
"동기화"를 위한 세마포어의 초기값 : 0
p1 :
-- --
signal(synch);
p2:
wait(synch); //0보다 작거나 같으면 busy_waiting
-- --
** 추가적으로 mutex와 비교되는 semaphore의 특징은 임계영역에 n개의 진입을 허용하는 counting semaphore가 존재한다. 세마포어의 값 n이 임계영역에 들어갈 수 있는 스레드 개수를 뜻하게 됨
'CS > OS' 카테고리의 다른 글
Unix 명령어 모음집.zip (6) | 2024.10.21 |
---|---|
스케쥴링 - 어떤 프로세스에게 CPU를 줄까요~? (1) | 2024.09.15 |
Thread 스레드 - 하나의 프로세스 안에 멀티스레드 (0) | 2024.09.13 |
SSH와 포트포워딩으로 외부에서 원격 접속하기 (0) | 2024.09.13 |
리눅스랑 더 친해지기! vim / gcc / make (0) | 2024.09.13 |