일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Waterfall
- protection
- frequency-domain spectrum analysis
- polymorphism
- FIFO
- information hiding
- AINCAA
- 유스케이스
- 게임 개발
- Security
- 컴퓨터 네트워크
- link layer
- DP
- OSI 7계층
- SJF
- MLFQ
- MAC
- 유니티
- Unity #Indie Game
- unity
- 게임개발
- 메카님
- Trap
- stride
- 배경 그림
- OWASP
- DSP
- SDLC
- 운영체제
- STCF
- Today
- Total
다양한 기록
멀티 스레드 / 경쟁 상태, 공유자원, 임계영역, 상호배제 본문
스레드: 제어의 흐름
프로세스: 제어의 흐름 + 수행을 하기 위한 자원
멀티 스레드 프로세스: 제어의 흐름들 + 수행을 하기 위한 자원
-> 자원의 공유가 발생, 경쟁. 컨커런시 제어 필요
컴퓨터 자원
- CPU(레지스터)
- 어드레스 스페이스 (코드 데이터 스탭 힙)
- 파일
어떻게 이런 자원을 관리하느냐
프로세스 모델
- 배타적으로 자원을 사용
- fork는 모든 자원을 다 새로 만듦
스레드 모델
- 필요한 자원들 중 몇몇만 공유, CPU나 스택은 따로 가짐
- 공유: 코드, 데이터, 힙, 파일
당연히 스레드가 더 빠르고, 공유하는 측면에서도 스레드가 좋음
-> 속도가 유리
프로세스는 한 프로세스가 죽어도 다른 프로세스에 영향을 안끼치지만,
스레드는 같은 프로세스 내 한 스레드가 죽으면 다 죽음
-> 스레드는 결함의 고립(isolation)에 불리
스레드의 장점
- 만들기 빠름 : 프로세스는 헤비 웨이트, 스레드는 라이트 웨이트
- 패럴리즘 : 소팅으로 예시를 들면 멀티스레드의 경우 분할 정복하면 됨 (맵 리듀스)
- 웨이팅 오버랩하기 쉬움 : 서버가 요청을 받으면 커넥션 만들고 스레드 만들어서 실행시키고 자기는 다른 요청 다시 기다리고..
- 데이터 쉐어링 쉬움
스케줄링의 대상으로서는 같은 걸로 여겨지지만(스케줄링 엔티티), 데이터 공유의 관점에서 다름
스레드는 TCB를 가지고, 프로세스처럼 상태전이도 하고, 자신만의 priority를 가질 수 있음
이러한 특징 상 스케줄링 정책 따라 스레드 중 뭐가 출력될 지가 알 수가 없고,
만들고 나면 컨트롤이 안됨
결과가 계속 다르고 정답도 아닌 결과가 나오기도 함(non-determistic) -> 타이밍 버그
타이머 인터럽트는 비동기적인 사건(어느 시점이든지 발생할 수 있음)
기계어 수준에서 스케줄링 되는 걸 보면 원자적으로 실행되지 않아 미싱이 발생하기도 함
해결책 -> 원자성
모든 걸 하거나, 안하거나.
크리티컬 섹션(경쟁 상태를 야기하는 프로그램 코드) 부분은 원자적으로 처리되어야 함
#include<stdio.h>
#include<pthread.h>
static volatile int counter = 0;
void* mythread() {
for(i = 0; i < 1e7; i++) {
counter = counter + 1;
}
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
pthread_create(&p1, NULL, mythread, NULL);
pthread_create(&p2, NULL, mythread, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
return 0;
}
1. 공유 자원
2. 임계 영역 (critical section)
3. 경쟁 상태 (race condition)
4. 상호 배제 (mutual exclusion)
위 코드에서 공유 자원은 전역변수 counter, 임계 영역은 mythread()의 counter = counter + 1이 될 것이고,
이 코드 앞 뒤로 락, 언락을 해주면 상호 배제가 될 것이다. 두 스레드가 카운터에 동시에 접근하려는 상태가 경쟁상태이다.
Concurrency의 두가지 이슈
1. 한 순간에 임계영역에 들어오는 스레드는 하나여야 한다
2. 동기화 : 한 스레드의 어느 부분은 항상 다른 스레드를 기다려야 한다
스레드 API
유저레벨 스레드: 2000년 초에 좀 쓰였고 요즘엔 안 씀
커널레벨 스레드
- 커널에서 스레드를 지원
- pthread, 리눅스 스레드, 자바스레드..
pthread
- 생성
- 대기
- 뮤텍스락, 언락
- 조건변수
* 스레드를 만들 때 어떤 값을 만들어서 리턴해야 하는 경우
=> 동적할당해서 넘겨야 함
리턴 값이 스택에 들어가면 스레드 끝나는 순간 날아가니까 주의
컨커런시 메커니즘
1. 락(lock)
// example
pthread_mutex_t lock;
int rc = pthread_mutex_init(&lock, NULL);
assert( rc == 0 );
pthread_mutex_lock(&lock);
x = x + 1;
pthread_mutex_unlock(&lock);
상호배제를 위함. 락과 언락의 사이는 원자성이 보장
2. 동기화 (synchronization)
// thread 1
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_lock(&lock);
while(ready == 0)
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
// thread 2
pthread_mutex_lock(&lock);
ready = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
wait와 signal을 하려면 우선 락을 해야 함. 조건 변수를 쓰기 전 락은 필수
스레드 둘 중 뭐가 먼저 실행될지는 몰라도, 스레드 2의 윗부분이 스레드 1의 밑부분보다 먼저 실행되는게 보장됨
순서 관계가 보장되는 경우
* 조건이 맞지 않으면 계속 웨이트..
그런데 락 걸고 웨이트 들어갔는데 어떻게 락이 또 걸리냐?
wait 들어가기 전에 락을 풀어주고, 깨어날때 다시 잠금
=> 데드락이 예방됨
'운영체제' 카테고리의 다른 글
Lock-based Concurrent Data Structure (0) | 2024.04.18 |
---|---|
Lock의 구현 (0) | 2024.04.11 |
Cache Affinity / job의 실행 시간 예측 (0) | 2024.04.08 |
Proportional Share : 로터리 스케줄링, 스트라이드 스케줄링 (0) | 2024.04.08 |
스케줄링 과정 그려보기, ART, ATT (0) | 2024.03.26 |