일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- uuid-ossp
- enable_if
- adminbro
- mistel키보드
- 어셈블리
- psql extension
- 42seoul
- c++
- 스타트업
- 파이썬
- 레이캐스팅
- 프라이빗클라우드
- 엣지컴퓨팅
- GraphQL
- 텍스트북
- 부동소수점
- 42서울
- Cloud Spanner
- 스플릿키보드
- 어셈블리어
- 창업
- schema first
- 자료구조
- 도커
- 이노베이션아카데미
- SFINAE
- raycasting
- 정렬
- 쿠버네티스
- 동료학습
- Today
- Total
written by yechoi
[동기화] 데이터 레이스란? (확인하는 법, 해결법) 본문
데이터 레이스란?
데이터 레이스란 멀티 쓰레드/프로세스 환경에서 일어나는 오류다. 여러 쓰레드/프로세스가 공유자원에 동시에 접근하려 할 때, 일어나는 경쟁 상황을 일컫는다.
가령 한 쓰레드와 또 다른 쓰레드가 동시에 한 변수를 쓰려고 할 때, 한 쓰레드는 한 변수를 쓰고 있는데 다른 쓰레드는 그 변수를 읽으려고 할 때 등의 상황에서 데이터 레이스가 발생한다.
아래는 간단한 예시다.
g_num = 0;
void add(void)
{
g_num++;
}
모든 쓰레드가 접근할 수 있는 g_num이 있고, add함수는 g_num을 1 증가시킨다. 서로 다른 두 쓰레드가 add 함수를 호출한다면 초기에 0이었던 g_num의 값은 어떻게 될까?
printf(g_num);
우리가 예상하는 값은 한 쓰레드에서 +1 또 다른 쓰레드에서 +1 을 했으므로 g_num은 2일 것으로 기대한다.
그러나 예상과 다르게 g_num의 값은 1이 되는 현상이 발생한다.
데이터 레이스 해결법
공유자원을 안전하게 관리하기 위해선 '상호배제' 기법이 필요하다. 공유자원을 모든 쓰레드/프로세스가 원하는 때 언제든지 접근할 수 있도록 허용하지 않고, 쓰레드/프로세스의 접근을 제한적으로 허용하는 방식이다.
이러한 상호배제를 달성하기 위해선 뮤텍스, 세마포어를 알아야 한다. 뮤텍스와 세마포어에 대한 쉬운 설명은 여기 를 참고했다.
간단하게 뮤텍스를 바탕으로 설명하자면
void add(void)
{
pthread_mutex_t m;
pthread_mutex_init(&m, NULL);
pthread_mutex_lock(&m);
/*임계구역 시작*/
g_num++;
/*임계구역 끝*/
pthread_mutex_unlock(&m);
}
pthread_mutex_lock
을 한 이후부터 pthread_mutex_unlock
을 할 때까지 임계구역(critical section)이 설정된다. 즉 이 구역에는 쓰레드/프로세스가 하나만 들어갈 수 있다.
어떻게 확인할까?
눈으로 코드를 따라가며 확인할 수도 있지만, 컴파일 시 플래그를 주면 아주 쉽게 확인 가능하다. 컴파일 옵션으로 -g -fsanitize=thread 를 주기만 하면, 친절하게도 데이터 레이스가 발생한 위치와 이유를 알려준다.
$ gcc source.c -g -fsanitize=thread
이런 식으로.
모든 데이터 레이스를 해결해야 하나?
데이터 레이스를 해결하기 위해 뮤텍스/세마포어를 여기저기 걸다보면 부작용이 생긴다. 처리 시간이 느려진다. 처리 시간의 구애를 받지 않는 프로그램이라면 상관이 없겠지만, ms 단위가 중요한 프로그램이라면 문제가 될 수 있다. (딜레이에 관한 구체적인 상황을 참고하고 싶다면 이곳 을 확인해보면 좋을 듯!)
이유인 즉슨 쓰레드/프로세스의 컨텍스트 스위칭(context switching) 이 일어나기 때문이다. CPU는 동시에 한 개씩의 쓰레드/프로세스만 실행이 가능하다. 멀티 쓰레드/멀티 프로세스란 단지 빠른 시간 동안 여러가지의 쓰레드/프로세스가 순차적으로 실행하면서, 동시에 실행되는 것처럼 보이는 것이다.
이처럼 순차적으로 실행되는 과정에선 비용이 발생한다. 뮤텍스의 관점에선 먼저 자원을 점유하고 있던 쓰레드/프로세스가 뮤텍스(세마포어)를 언락(포스트) 했을 때, 대기하고 있던 다른 쓰레드/프로세스가 깨어나면서 컨텍스트 스위칭이 일어난다. 이러한 컨텍스트 스위칭이 잦으면 오버헤드가 발생해 성능이 떨어진다.
나의 경우는 모든 데이터 레이스를 해결하는 게 아닌, 선별적으로 경우를 나눠 임계영역을 설정해줬다. 치명적이라고 생각하는 데이터 레이스만 처리해준 것이다. 어디까지나 나의 경우이고 해결은 각자의 몫이다. 모든 데이터 레이스를 해결해 안전한 코드를 만들 것인지, 어느정도의 데이터 레이스를 허용하며 성능 저하를 막을지 말이다.