본문 바로가기
42/42cursus

[philosophers] 4. alt_usleep의 함정

by jaemjung 2022. 2. 19.

철학자들의 교착상태와 기아상태를 해결해 준 줄만 알고 있었던 나는 평가를 받기 전 혹시나 하는 마음에 인자 값을 바꿔가며 여러번 테스트를 돌려보았다.

 

그러던 중 이상한 현상을 발견했으니... 바로 터미널에서 철학자 시뮬레이션을 돌리던 중 다른 창을 클릭해서 넘어가면 랜덤하게 시뮬레이션의 시간이 몇 ms정도 지연되면서 철학자가 죽어버리는 것이었다!

그 뿐만이 아니라 철학자의 수가 30명이 넘어가면 죽어서는 안되는 케이스에서 10초를 못 버티고 철학자들이 픽픽 쓰러지고 있었다..

 

대체 왜 이러는 것인지 알 수가 없어 슬랙에 도움을 요청했는데, 어떤 분께서 usleep에 문제가 있는 지 확인해보라고 하셨다.


본 과제에서는 철학자들이 식사를 하는시간, 잠을 자는 시간 동안만큼 각 스레드의 동작을 지연시켜줘야 한다.

이러한 지연에는 여러 가지 방법이 있겠지만, 내가 가장 먼저 떠올린 것은 과제에 허용된 usleep함수를 이용하는 것이었다.

usleep은 마이크로 초 단위로 코드의 실행을 지연시켜주는 함수였으므로, 그것을 밀리초로 변환하여 인자로 들어온 값 만큼 usleep을 시켜주면 되는 것 아닌가? 라고 생각했지만..

usleep은 생각보다 오차가 심한 함수였다는 문제가 있었다.

usleep 안에 들어가는 값이 커질 수록 그 오차값 역시 커졌는데, 이러한 오차가 누적되다 보면 출력되는 로그의 시간값이 뒤엉킬 뿐만 아니라 철학자가 죽지 않아야 하는 상황에서도 usleep으로 인한 오차로 죽게 되는 상황이 생겼다.

 

따라서 해결 방법을 모색하던 중 busy-waiting 방식으로 현재 시간과 목표로 하는 시간을 계속 비교해가며 목표 시간이 되면 함수를 탈출하는 형태의 대체 sleep을 할 수 있는 방법을 찾게되었다.

 

void	alt_sleep(long long time_to_sleep)
{
	long long	target_time;

	target_time = get_time() + time_to_sleep;
	while (target_time > get_time())
		;
}

 

나도 옳다꾸나 하고 이 함수를 usleep 대신 적용하여 프로그램을 작성하였는데, 이 전까지 무수한 시간 오차값들로 지저분했던 로그가 깔끔하게 딱 떨어지는 것을 보고 감탄했었다.

하지만 이 alt_sleep함수는 리소스를 엄청나게 먹는다는 단점이 있었다. 그럴 수 밖에 없는게, 지정한 시간까지 계속 시간을 불러와 비교하는 연산을 반복하게 되니, cpu 점유율이 높아져버리는 것이다. 게다가 그걸 수십개, 수백개의 스레드에서 반복하고 있으니...

 

해결 방법은 의외로 간단했다. 저 반복문 안에서 짧은 시간만큼 usleep을 해주는 것!

 

void	alt_sleep(long long time_to_sleep)
{
	long long	target_time;

	target_time = get_time() + time_to_sleep;
	while (target_time > get_time())
		usleep(100);
}

 

이런 식으로 목표로 하는 target_time이 오기까지 미친듯이 계속 비교를 하며 대기를 하는 것이 아닌, 100us만큼 기다리고 비교를 하는 식으로 하면 비교에 사용되는 자원을 확 낮출 수 있었다.

실제로 이렇게 하니 다른 창을 클릭해 컨텍스트 스위칭이 일어나더라도 철학자들이 죽지 않고 잘 돌았고, 200명 시뮬레이션을 돌려도 웬만큼 잘 살아있게 되었다.


 

이제 남은 것은 평가뿐...

 

그러나 나는 본 과제에서 금지하는 libft를 사용하여 평가는 처참히 멸망하였고,

이왕 이렇게 된 김에 보너스에 도전하여 process와 semaphore를 사용하는 시뮬레이션 만들기를 시작하게 되는데...

댓글