1.4 스레드 정체

컨텍스트 스위칭 (context switch)

- 각 스레드를 실행하다 말고 다른 스레드를 마저 실행하는 과정

- 여러 프로세스와 여러 스레드를 동시에 실행해야 하는 운영체제는

  여러 프로세스와 여러 스레드를 일정시간마다 번갈아가면서 실행

컨텍스트 스위치의 단점

적지 않은 양의 연산이 발생한다

원인

실행 중이던 스레드의 상태(호출 스택 등)를 어딘가에 저장하고 과거 실행하다가 만

다른 스레드 중에서 하나를 골라 스레드 상태를 복원하고 다음 실행 지점으로 강제 이동해야한다

문제

컨텍스트 스위치에 연산량이 많아 동시에 실행해야  하는 스레드가 많이 있어도

컨텍스트 스위치를 많이하게 되면 더 비효율적인 프로그램이 된다.

해결 방법

1. 컨텍스트 스위치를 최대한 덜한다

문제점 : 사람의 입장에서 갑갑하다 

2. CPU의 수가 스레드의 수가 같거나 더많은 경우 

문제점 : 문제점 없다 컨테스트 스위치가 일어나지 않는다

3. CPU의 수 보다 스레드의 수가  더 많은 경우 

문제점 : 컨테스트 스위치가 반드시 발생하여 성능 문제로 이어진다

 

결론

프로그램의 스레드가 많더라도 실행중인 스레드의 개수가 CPU의 개수보다 적다면 문제가 없다

1.5 스레드를 다룰 때 주의 사항

데이터 레이스, 경쟁상태

- 두 스레드가 데이터에 접근해서 그 데이터 상태를 예측할 수 없게 하는 것

- 부정확한 연산 결과를 초례하는 원인

문제

컨테스트 스위치는 기계어 명령어 단위로 일어나는데 두 스레드가 하나의 데이터에 접근하는 과정에서

컨테스트 스위치가 일어나게 되는 경우 메모리에 대한 충돌이나 부정확한 연산 결과가 발생한다

해결

원자성과 일관성을 유지하여 특수한 조치를 한다

특수한 조치 : 동기화 (임계영역과 뮤텍스, 잠금 기법)

 

1.6 임계 영역과 뮤텍스

뮤텍스 (mutex)

- 뮤텍스는 상호 배제(mutual exclusion)의 줄임말

- 뮤텍스는 다른 말로 임계 영역 (critical section) 이라고도 한다

- 뮤텍스 사용권을 얻는 과정을 잠근다(lock) 이라고 표현한다.

- 뮤텍스 사용권을 반납 하는 과정을 잠금해제 (unlock) 이라고 표현한다.

* 윈도우에서는 뮤텍스보다 임계 영역이 처리 속도가 빨라 임계 영역 사용을 권장하는 편

  리눅스에는 뮤텍스 밖에 없지만 임계 영역처럼 작동할 수 있도록 최적화 되어 있다.

//mutex 사용 예
std::mutex mx;
mx.lock();
read(x);
write(y);
sum(x);
mx.unlock();

스레드1 이 lcok(); 에 접근하여 사용권 여부를 확인한다

스레드2 가 이미 접근하여  사용권을 가지고 있다면 스레드1 은 정지한다

스레드2 가 작업을 다 마치고 mx.unlock() 를 통해 사용권을 반납하면

스레드1 이 사용권을 부여 받고 작업을 시작한다.

 

lock_guard

- C++ 에서 제공하는 클래스

- 뮤텍스 잠금 상태를 로컬 변수로 저장하고 변수가 사라질 때 자동으로 잠금 해제시킨다.

- 뮤텍스 사용권을 가지고 있는 상태에서 try~catch문과 같은 예외가 발생하여 unlock()을 하지 못하는 것을 방지한다.

//lock_guard 사용예
std::recursive_mutex mx;
lock_guard<recursive_mutex> lock(mx);
read(x);
write(x);
sum(x);
//로컬 변수 lock 객체가 사라질 때 자동으로 mx.unlock() 실행

//c# 의 경우
//1. (lock)보호하려는 변수 자체를 뮤텍스처럼 사용하여 잠그거나 변수를 가르키는 임의의 객체르르 따로 만들어 잠근다
//2. (unlock)다 쓰고 나면 잠금을 해제
object mx = new object();
lock(mx)
{
	read(x);
	write(y);
	sum(x);
}

//c++ 의 경우
1. 보호 하려는 변수들을 위한 뮤텍스용 객체 mutex를 생성 
2. mutex 객체를 lock() gkatnfh 잠금
3. 다 쓰고 나면 unlock() 함수로 잠금을 해제
std::mutex mx;
{
	std::lock_guard<std::mutex> lock(mx);
    read(x);
    write(y);
    sum(x);
}

뮤텍스를 최대한 잘게 사용하는 경우?

1. 프로그램 성능이 떨어진다. 뮤텍스를 엑세스하는 과정 자체가 무겁기 때문에

2. 프로그램이 매우 복잡해진다. 교착상태 문제가 쉽게발생하기 때문에

뮤텍스를 큰 범위로 넓게 잡아 사용하는 경우?

- 싱글스레드로 작동하는 프로그램과 다를 바가 없다. 멀티 스레드 사용의 이유가 없다 그럼 뮤텍스도 쓸 필요없다.

 

 

*컨텐션(contention)이란?

- 두 스레드가 동시에 한 데이터를 액세스하려고 하는 상황을 의미

- 뮤텍스로 잠금하여 보호하지않으면 경쟁 상태가 발생

- 뮤텍스로 잠금하였을 때 다른 스레드가 모두 대기하여 병렬성이 사라지는 문제가 발생

- 멀티 스레드 프로그래밍에서는 불가피한 문제

 

+ Recent posts