Search
Duplicate
🥊

소인배 필로소퍼 구현하기 ycha 버전

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
C
42seoul
Scrap
태그
multithread
9 more properties
문제 이해는 했다 치고 바로 구현하는 법 설명 들어갑니다

0. 들어가기전에

제가 필로소퍼를 구현하면서 했던 방식을 간략하게 써보았습니다.
아래의 설명대로 구현해도 완벽한 필로소퍼가 나오지 않습니다.
대략적인 흐름을 이해하기위한 설명정도로만 이해하고 넘어가주세요
내용이 정확하지 않을수있습니다. 얼마든지 지적 부탁드립니다. 감사합니다.

1. 헤더 구상

대충 필로소퍼를 구현하는데 있어서, 뭐가 필요할지 생각해봅니다

1-1. 입력 구초제 구현

다음과 같은 구조체를 만들어 입력을 받습니다 (이하 입력값 구조체라고 부름)
typedef struct s_arg { int number; int time_to_die; int time_to_eat; int time_to_sleep; int must_eat_count; } t_arg;
C
복사

1-2. 공유 구조체 구현

필로소퍼들이 핵심적으로 공유할 것
포크 배열 (뮤텍스로 보호된 값)
그 이외에 필요한것
시뮬 시작 시간 (시간을 출력할떄 필요함)
출력 뮤텍스 (출력 안꼬이게 하려고)
시뮬레이션이 종료 여부 확인값 (Boolean)

1-3. 필로소퍼 구조체 구현

필로소퍼가 가지고 있어야할것
자기 번호
양옆 포크 번호
먹은 횟수
죽을 시간
공유 구조체
입력값 구조체
쓰레드

2. 입력 구현

위에서 만든 입력값 구초제에 값을 넣는걸 구현합니다.

3. 시뮬레이션

main이든 전역변수든 싱글톤이든 계속 들고다녀야할것
입력값 구조체
공유 구조체
필로소퍼 구조체 배열

3-1. 초기화

1.
공유 구조체를 초기화합니다.
a.
포크 구조체 배열을 갯수만큼 초기화 합니다. (malloc)
b.
시뮬 시작 시간 초기화
c.
출력 뮤텍스를 초기화한다.
d.
시뮬레이션이 종료 여부 확인값을 false 로 둔다.
2.
필로소퍼 구조체 배열도 초기화 합니다.
a.
자기 번호 값 정해주기
b.
왼쪽 오른쪽 포크 번호 정해주기
c.
먹은횟수는 0, 죽을 시간은 시뮬 시작시간 + time_to_die 으로
d.
공유 구조체와 입력값 구조체 초기화
e.
아직 쓰레드는 초기화 안함!

3-2. 시작

아까 필로소퍼 구조체가 가지고있던 쓰레드 변수에 쓰레드 생성한다.
여기서 detach 하고 싶으면 detach 한다.

3-2-1. 쓰레드 계획

모니터 쓰레드를 두고, 모니터가 계속 필로소퍼가 죽었는지, 다 먹었는지 감시하는 방식으로 구현합니다.
이떄, 모니터 쓰레드를 두는 방식에는 2가지가 있는데,
방법1
방법2
메인 쓰레드 → 필로소퍼 1 → 필로소퍼 2 → 필로소퍼 3 → 필로소퍼 4 → 모니터 쓰레드
메인 쓰레드 = 모니터 → 필로소퍼 1 → 필로소퍼 2 → 필로소퍼 3 → 필로소퍼 4
저는 방법 2 로 구현하겠습니다
이제 쓰레드가 돌아갈 필로소퍼 함수를 구현해보자

3-2-2. 필로소퍼 함수

1.
먹기
a.
포크 집기
i.
여기서는 다양한 해결방법이 나올수있습니다.
ii.
짝수는 오른쪽포크, 홀수는 왼쪽포크를 먼저 집는형식으로 구현하기도 합니다.
iii.
저는 이렇게 구현했습니다.
b.
죽을 시간 연장
c.
먹은 횟수 증가
d.
메세지 출력
e.
먹는동안 usleep
f.
먹고나면 포크 내려놓기
2.
자기
a.
메세지 출력
b.
자는동안 usleep
3.
생각하기
a.
메세지 출력
4.
시뮬레이션이 종료되지 않았다면, 반복

3-2-3. 모니터 함수

1.
필로소퍼 죽었나 확인
a.
필로소퍼 구조체 배열을 돌면서 죽을 시간을 넘긴 필로소퍼가 있다면,
b.
메세지 출력
c.
시뮬레이션이 종료 여부 확인값을 true로 변경
d.
return
2.
다 먹었나 확인
a.
필로소퍼 구조체 배열을 돌면서, 다먹었는지 확인
b.
메세지 출력
c.
시뮬레이션이 종료 여부 확인값을 true로 변경
d.
return

3-3. 종료

메모리 해제할거 다 해제하고 프로그램 정상 종료
뮤텍스 지울거 다 지우고
혹시 쓰레드 join 할거 있으면 join 하고
free 할거 있으면 다 free 하고

4. detach? join? 둘중 머쓰지

쓰레드의 자원을 회수하는 방법에는 2가지가 있습니다.
detach : 해당 쓰레드가 종료되면 알아서 자원 회수
join : 메인 쓰레드에서 해당 쓰레드가 종료될때까지 기다렸다가 자원 회수
2가지 방법 모두 구현가능하지만, 장단점이 있는데요,
제가 개인적으로 구현하면서 느낀점을 정리해보았습니다.
detach
join
장점
쓰레드가 종료되는것을 기다리지 않아도 되기 때문에, 시뮬레이션을 바로 종료할 수 있습니다.
쓰레드가 모두 종료된후에 안전하게 fork 를 free 할 수 있습니다.
단점
시뮬레이션을 종료하면서 fork 를 free하는 시점에 쓰레드중 하나가 fork 에 접근한다면 segfault가 날 수 있습니다.
메인 쓰레드가 기다리기 때문에 시뮬레이션이 바로 종료되지 않을수있습니다.

5. 200명 욕심내기

모든 while 문은 원활한 context switching 을 위해서 usleep을 조금씩 넣어줍니다. (약 200정도?)
200명 정도 되면, 쓰레드가 생성되는데 시간이 꽤 걸립니다.
그래서 순차적으로 포크를 잡고 않고 꼬일 수 있기 떄문에, 필로소퍼가 처음에 포크를 들기전에,
홀수번째 필로소퍼는 time_to_eat 만큼정도 기다려주고 먹으면 덜 꼬입니다.