식사하는 철학자들 🍝
- 한 명 이상의 철학자들이 원탁에 둘러앉아 있다. 원탁에 큰 그릇의 스파게티와 철학자 수만큼의 포크가 있다.
- 철학자들은 항상 먹기, 잠자기, 생각하기 중 하나의 상태를 가진다.
- 철학자가 스파게티를 먹으려면, 양손에 포크를 들어야 한다.
- 철학자가 식사를 마치면, 포크를 내려놓고 잠을 잔다.
- 철학자가 잠에서 깨어나면, 생각을 하기 시작한다.
- 철학자가 굶어 죽으면 시뮬레이션이 중단된다.
- 철학자들은 서로 이야기할 수 없다.
과제 규칙
- 전역 변수 금지
- 프로그램 인자
: number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]- number_of_philosophers : 철학자와 포크의 수로, 각 철학자는 1번부터 number_of_philosophers번까지의 번호를 부여받는다. : 둘러앉아 있으므로 1번 철학자 옆에는 2번 철학자가, ..., number_of_philosophers번 옆에는 1번 철학자가 앉아있다
- time_to_die(ms) : 철학자가 마지막 식사를 시작하거나 시뮬레이션을 시작한 이후로 time_to_die(ms) 안에 먹기 시작하지 않으면, 죽는다 ☠️
- time_to_eat(ms) : 철학자가 먹는 데 소요되는 시간 😋
- time_to_sleep(ms) : 철학자가 잠을 자는 데 소요되는 시간 😴
- number_of_times_each_philosopher_must_eat : 선택적 인수로, 모든 철학자가 number_of_times_each_philosopher_must_eat번 식사를 하면 시뮬레이션이 종료된다. 지정되지 않은 경우 철학자가 굶어 죽을 때까지 시뮬레이션이 계속 되어야 한다.
- 프로그램 로그
: 철학자의 상태 변경이 있을 때마다 다음과 같은 형식의 로그를 출력해야 한다. timestamp_in_ms를 현재 타임스탬프(ms)로 바꾸고, X를 철학자 번호로 바꾼다.- timestamp_in_ms X has taken a fork : 포크를 들 때마다 출력해야 한다 ! 왼손 한 번, 오른손 한 번 ...
- timestamp_in_ms X is eating
- timestamp_in_ms X is sleeping
- timestamp_in_ms X is thinking
- timestamp_in_ms X died
- 상태 표시 메시지는 다른 메시지들과 섞이면 안된다.
- 철학자가 사망했음을 알리는 메시지는 실제 철학자가 사망한 후 10ms 이내에 표시되어야 한다.
- 철학자가 죽는 것을 피해라 !
Mandatory
- 각각의 철학자는 스레드여야 한다.
- 철학자들 사이에 하나의 포크가 있다. 그러므로 여러 철학자가 있다면, 각각의 철학자 왼쪽과 오른쪽에 포크가 있다. 철학자가 한 명뿐이라면 포크가 한 개만 있다.
- 철학자가 포크를 복제하지 못하도록 하려면 포크 상태를 각 포크에 대한 뮤텍스로 보호해야 한다.
Mandatory 허용 함수
usleep
#include <unistd.h>
int usleep(useconds_t usec);
최소 usec 마이크로초 동안 호출 스레드의 실행을 중단한다.
gettimeofday
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tv, struct timezone *restrict tz);
struct timeval {
time_t tv_sec; /* seconds since Jan. 1, 1970 */
suseconds_t tv_usec; /* and microseconds */
};
현재 시간을 tv 구조체에 저장한다.
- 두번째 인자인 timezone 구조체는 현재 사용되지 않으며, 앞으로도 지원되지 않을 것이다. NULL을 넣어주면 된다.
pthread_create
#include <pthread.h>
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg
);
호출 프로세스 내에서 새 스레드를 생성하고, 생성된 스레드는 start_routine(arg)을 실행한다.
- 매개 변수
- pthread_t *thread : 스레드의 식별자
- const pthread_attr_t *attr : 설정할 스레드 속성 정보 (NULL 입력시 기본 설정 적용)
- void *(*start_routine)(void *) : 스레드가 실행할 함수
- void *arg : start_routine() 함수의 인자
- 생성된 스레드는 다음 3가지 경우에 종료된다.
- pthread_exit() 호출
- start_routine() 수행 후 return : Philosophers 과제는 여기에 해당 !
- pthread_cancle() 호출
pthread_detach
#include <pthread.h>
int pthread_detach(pthread_t thread);
thread가 나타내는 스레드를 분리 상태로 만든다.
- 분리된 스레드가 종료하면 자원이 자동으로 해제된다.
pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
thread로 지정한 합류 가능 상태인 스레드가 종료하기를 기다린다. 스레드가 이미 종료됐으면 즉시 리턴한다.
- retval이 NULL이 아니면 대상 스레드의 종료 상태를 retval이 가리키는 위치로 복사한다.
pthread_mutex_init
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
뮤텍스 객체를 초기화한다.
- 매개 변수
- pthread_mutex_t *mutex : 뮤텍스의 식별자
- const pthread_mutexattr_t *attr : 설정할 뮤텍스 속성 정보 (NULL 입력시 기본 설정 적용)
pthread_mutex_destroy
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex가 가리키는 뮤텍스 객체를 파기한다.
pthread_mutex_lock
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
뮤텍스 식별자가 mutex인 뮤텍스를 잠근다.
- 뮤텍스가 이미 다른 스레드에 의해 잠겨 있으면 뮤텍스가 사용 가능해질 때까지 호출 스레드는 블록된다.
- 잠겨 있는 뮤텍스를 파기하려는 시도의 결과는 규정되어 있지 않다.
pthread_mutex_unlock
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
뮤텍스 식별자가 mutex인 뮤텍스가 잠금 상태이면, 잠금을 해제한다.
- 잠금이 해제되어 뮤텍스가 사용 가능해질 때 해당 뮤텍스 객체에 블록된 스레드들이 있으면 스케줄링 정책에 따라 그 뮤텍스를 획득할 스레드가 정해진다.
- 잠겨 있지 않은 뮤텍스를 파기하는 것은 안전하다.
'42Seoul' 카테고리의 다른 글
[Philosophers] 🚨 usleep 함수의 문제점 ? (0) | 2023.05.09 |
---|---|
[Philosophers] ⚖️ pthread_join vs pthread_detach (2) | 2023.05.09 |
[Philosophers] 2. 스레드와 뮤텍스 (0) | 2023.04.14 |