- 스레드 동기화 필요성
- 상호 배제
- 멀티스레드 동기화 기법
- 생산자 소비자 문제
다수의 스레드가 동시에 공유 데이터에 접근할 경우 공유 데이터가 훼손될 수 있음!
☞ 스레드 동기화
: 공유데이터 훼손되는 문제 해결책
:공유 데이터를 접근하고자 하는 다수의 스레드가 충돌 없이 공유데이터에 접근하기 위해 상호 협력하는 것
-> 한 스레드가 공유데이터를 배타적 독점적으로 접근
공유 데이터에 대한 멀티스레드의 접근 문제와 동일함
스레드 동기화를 통해 한 스레드가 공유데이터에 대해 배타적이고 독립적으로 접근하도록 허용되어야 함
스레드 동기화
한 스레드가 공유 데이터 사용을 마칠 때까지 다른 스레드가 접근하지 못하도록 제어하는 기법
공유 집계판 사례를 멀티스레드로 구현하여 공유데이터가 훼손되는 상황 확인
#include <stdio.h>
#include <pthread.h>
int sum = 0; // 두 스레드가 공유하는 변수
void* worker(void* arg) { // 스레드 코드
for(int i=0; i<1000000; i++) {
sum = sum + 10;
}
}
int main() {
char *name[] = {"황기태", "이찬수"};
pthread_t tid[2]; // 2개의 스레드 ID를 담을 배열
pthread_attr_t attr[2]; // 2개의 스레드 정보를 담을 배열
pthread_attr_init(&attr[0]); // 디폴트 속성으로 초기화
pthread_attr_init(&attr[1]); // 디폴트 속성으로 초기화
pthread_create(&tid[0], &attr[0], worker, name[0]); // 스레드 생성
pthread_create(&tid[1], &attr[1], worker, name[1]); // 스레드 생성
pthread_join(tid[0], NULL); // 스레드 종료 대기
pthread_join(tid[1], NULL); // 스레드 종료 대기
printf("total sum = %d\n", sum); // 두 스레드의 종료 후 sum 출력
return 0;
}
실행할 때마다 sum 값이 달라지며, 20,000,000이 되지 않음
공유 데이터에 대한 동시 접근 문제의 해결책
문제점
: 여러 스레드가 공유 데이터에 동시 접근하면 훼손 가능성 있음
해결책
: 스레드 동기화; 한 스레드가 접근 마칠 때까지 다른 스레드가 접근 못하도록 제어
다수의 스레드가 공유 데이터를 사용하려고 할 때, 먼저 접근한 스레드가 공유 데이터를 배타적으로 사용하도록 다른 스레드가 접근하지 못하게 상호 협력함
임계구역(critical section)
: 사용자가 작성한 프로그램 중 공유 데이터에 접근하는 코드 블록
: 반드시 한 스레드만 배타적 독점적으로 실행하도록 관리되어야 함
상호배제(mutual exclusion)
: 임계구역이 오직 한 스레드만 배타적(독점적)으로 사용되도록 하는 기술
: 임계구역에 먼저 진입한 스레드가 임계구역의 실행을 끝낼 때까지 다른 스레드가 진입하지 못하도록 보장
상호배제
: 멀티스레드가 실행되는 환경에서, 한 스레드가 임계구역 전체를 배타적으로 실행하도록 보장하는 기법
임계구역 전후에 상호배제 코드가 작성됨
일반 코드(non-critical code)
: 공유 데이터를 액세스하지 않는 코드 부분
임계구역 진입 코드(entry code)
: 상호배제를 위해 필요한 코드 (임계구역에 진입하기 전 필요한 코드 블록)
: 현재 임계구역을 실행 중인 스레드가 있는지 검사
: 없다면, 다른 스레드가 들어오지 못하도록 조치
: 있다면, 진입이 가능해질 때까지 대기
임계구역 코드(critical code)
: 공유 데이터에 접근하는 코드 블록
: 한 번에 한 스레드만 실행하도록 보장되어야 하는 프로그램 부분
: 짧을수록 좋으므로 공유 데이터를 액세스하는 최소한의 코드만 임계구역으로 만듬
임계구역 진출 코드(exit code)
: 상호배제를 위해 필요한 코드 (임계구역의 실행을 마칠 때 실행되어야 하는 코드 블록)
: entry code 에서 대기중인 스레드가 임계구역에 진입할 수 있도록 entry code 에서 취한 조치를 해제하는 코드
상호배제 요구조건
1. 오직 하나의 스레드만 임계역역에 진입할 수 있다
2. 임계영역이 아닌 곳에서 수행이 멈춘 스레드는 다른 스레드의 수행을 간섭하면 안된다
3. 임계영역에 접근하고자 하는 스레드의 수행이 무한히 미뤄지면 안된다
☞ 교착상태(deadlock) / 기아(starvation) 일어나면 안됨
4. 임계영역이 비어 있을 때, 진입하려고 하는 스레드가 지연되면 안된다
5. 프로세서의 개수나 상대적인 스레드 수행 속도에 대한 가정은 없어야 한다
6. 스레드는 유한 시간 동안만 임계영역에 존재할 수 있다
임계구역에 오직 1개의 스레드만 진입
임계구역 entry 코드에서 인터럽트 서비스를 금지하는 명령 실행
exit 코드에서 인터럽트를 허용하는 CPU 명령 실행
☞ 임계구역 실행하고 있는 동안 인터럽트 발생하지 않아 선점(preemption)되지 않음
문제점
원자명령
: 상호배제를 위해 만들어진 CPU 명령
원자명령 없이 lock 변수를 이용한 상호배제 시도
임계구역 들어갈 때 lock = 1 나올 때 lock = 0
entry 코드에서 lock = 1, exit 코드에서 lock = 0
lock 변수를 이용한 상호배제의 근본적인 문제
lock 변수를 사용한 경우 상호배제가 실패한 원인
entry 코드에서 lock 변수 값을 읽는 명령과 lock 변수에 1을 저장하는 명령 사이에 컨텍스트 스위치이 될 때 문제 발생
=> lock 변수를 읽어 들이는 명령과 lock 변수에 1을 저장하는 명령을 한번에 처리하는 원자 명령(TSL)
필요
멀티스레드 동기화
: 상호배제 기반
: 자원을 사용하려는 여러 스레드들이 자원을 원활히 공유하도록 하는 기법
locks 방식 - 뮤텍스(mutex), 스핀락(spinlock)
: 상호배제가 되도록 만들어진 락(lock) 활용
: 락을 소유한 스레드만 임계구역 진입
: 락을 소유하지 않은 스레드는 락이 풀릴 때까지 대기
wait-signal 방식 - 세마포(semaphore)
: n개의 자원을 사용하려는 m개 멀티스레드의 원활한 관리
: 자원을 소유하지 못한 스레드는 대기(wait)
: 자원을 다 사용한 스레드는 알림(signal)
- 잠김/열림 중 한 상태를 가지는 lock 변수 이ㅛㅇ
- 한 스레드만 임계구역에 진입 가능
- 다른 스레드는 큐에 대기
- sleep-waiting lock 기법
구성 요소
락 변수
: true/false 중 하나
: true - 락을 잠근다. 락을 소유한다.
: false - 락을 연다. 락을 해제한다.
대기 큐
: 락이 열리기를 기다리는 스레드 큐
연산
: lock 연산(entry 코드)
- 락이 잠김 상태(lock=true)이면, 현재 스레드를 블록 상태로 만들고 대기 큐에 삽입
- 락이 열림 상태(lock=false)이면, 락을 잠그고 임계구역 진입
: unlock 연산(exit 코드)
- lock=false, 락을 열린 상태로 변경
- 대기 큐에서 기다리는 스레드 하나 깨움
특징
멀티스레드 사이의 자원 관리 기법
- n개의 공유 자원을 다수 스레드가 공유하여 사용하도록 돕는 자원 관리 기법 / 동기화 기법
구성 요소
자원
: n개
대기 큐
: 자원을 할당받지 못한 스레드들 대기
counter 변수
: 사용 가능한 자원 개수 (n으로 초기화)
P/V 연산
P연산(wait 연산) - 자원 요청 시 실행 (자원 사용 허가)
V연산(signal 연산) - 자원 반환 시 실행 (자원 사용 끝 알림)
P 연산 : 자원 사용 허가, 사용 가능 자원 수 1 감소 (counter--)
V 연산 : 자원 사용 마침, 사용 가능 자원 수 1 증가 (counter++)
세마포 종류 (자원 할당받지 못한 경우의 행동에 따라 구분)
sleep-wait(수면 대기) 세마포
P연산: 대기 큐에서 잠자기
V연산: 사용가능 자원이 있으면 잠자는 스레드 깨워서 사용 허락
busy-wait(바쁜 대기) 세마포
P연산: 사용 가능 자원이 생길 때까지 무한 루프
V연산: counter--
- 공유 버퍼를 사이에 두고, 공유 버퍼에 데이터를 공급하는 생산자들과
- 공유 버퍼에서 데이터 읽고 소비하는 소비자들이 공유 버퍼를 사용할 때,
- 공유 버퍼를 문제 없이 사용하도록 생산자와 소비자를 동기화시키는 문제
- 멀티스레딩 응용프로그램 작성 시 자주 발생
해결해야되는 문제
비어 있는 버퍼 문제
꽉찬 공유 버퍼 문제