본문 바로가기

C Language

pthread_mutex_lock pthread_mutex_unlock 설명

이 코드는 철학자 문제와 같은 다중 스레드 환경에서 **뮤텍스 (mutex)**를 사용하여 자원을 안전하게 보호하는 코드입니다. 각 구성 요소와 코드 흐름을 자세히 설명해드리겠습니다.

코드 분석:

pthread_mutex_lock(philo->meal_lock);
if (get_current_time() - philo->last_meal >= time_to_die
    && philo->eating == 0)
    return (pthread_mutex_unlock(philo->meal_lock), 1);
pthread_mutex_unlock(philo->meal_lock);

1. pthread_mutex_lock(philo->meal_lock)

  • pthread_mutex_lock 함수는 지정된 뮤텍스를 잠급니다. 뮤텍스는 **상호 배제 (mutual exclusion)**를 의미하며, 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 합니다.
  • philo->meal_lock는 철학자 객체(philo)에 대한 뮤텍스입니다. 이 뮤텍스는 아마도 철학자의 last_meal 시간을 보호하는 데 사용되고 있을 것입니다. 이 뮤텍스가 잠금되면 다른 스레드(또는 철학자)가 이 자원에 접근할 수 없습니다.
  • 잠금이 성공적으로 이루어지면 코드가 실행되고, 다른 스레드는 이 뮤텍스를 해제할 때까지 대기합니다.

2. get_current_time() - philo->last_meal >= time_to_die 조건문

  • get_current_time() 함수는 현재 시간을 반환합니다.
  • philo->last_meal은 철학자가 마지막으로 식사를 한 시간을 나타내는 값입니다.
  • time_to_die는 철학자가 굶어 죽는 데 걸리는 시간을 나타냅니다.

이 조건문은 현재 시간과 마지막 식사 시간의 차이가 time_to_die 이상인지를 체크합니다. 즉, 철학자가 마지막 식사 이후로 죽을 시간이 지났는지 확인하는 부분입니다. 만약 철학자가 이미 죽었거나 죽을 시간이 지난 경우, 이후 코드에서 해당 철학자에게 더 이상 행동을 하지 않도록 할 수 있습니다.

3. && philo->eating == 0

  • 이 부분은 철학자가 현재 식사를 하고 있지 않은지를 확인하는 조건입니다.
  • philo->eating == 0은 철학자가 현재 식사 중이 아니라는 것을 의미합니다.
  • 즉, 철학자가 죽을 시간이 되었고, 현재 식사를 하고 있지 않다면 이 조건은 참이 됩니다.

4. return (pthread_mutex_unlock(philo->meal_lock), 1)

  • 조건이 참이면 이 코드가 실행됩니다.
  • pthread_mutex_unlock(philo->meal_lock): 뮤텍스를 해제합니다. 이는 pthread_mutex_lock으로 잠근 뮤텍스를 다른 스레드가 사용할 수 있도록 풀어주는 작업입니다.
  • return 1: 이 함수가 종료되고 1이 반환됩니다. 반환값 1은 아마도 철학자가 죽었다는 상태를 나타내는 값일 가능성이 큽니다.
  • 이 코드에서 pthread_mutex_unlock과 return은 한 줄로 연결되어 있습니다. 이때 (pthread_mutex_unlock(philo->meal_lock), 1) 구문은 먼저 pthread_mutex_unlock을 실행한 후 1을 반환하는 방식으로 실행됩니다. 이 구문은 pthread_mutex_unlock이 먼저 호출되도록 보장하면서 1을 반환하는 방식입니다.

5. pthread_mutex_unlock(philo->meal_lock)

  • 조건문을 만족하지 않으면 pthread_mutex_unlock(philo->meal_lock)을 실행합니다.
  • 이는 philo->meal_lock 뮤텍스를 해제하여 다른 스레드가 이 자원에 접근할 수 있게 합니다. 즉, 철학자가 죽었거나, 철학자가 아직 식사 중이라면, 이 뮤텍스를 풀고 다른 작업을 할 수 있도록 합니다.

전체 흐름 설명:

이 코드는 철학자 문제와 같은 다중 스레드 환경에서 각 철학자 객체에 대해 식사 시간을 추적하고 죽음 여부를 확인하는 부분입니다.

  1. 뮤텍스 잠금 (pthread_mutex_lock):
    • 철학자 객체의 meal_lock 뮤텍스를 잠급니다. 이를 통해 다른 스레드가 동시에 철학자의 last_meal 값에 접근하는 것을 막습니다.
  2. 죽음 여부와 식사 상태 확인:
    • get_current_time() - philo->last_meal >= time_to_die를 통해 철학자가 죽을 시간이 지났는지 확인합니다.
    • 또한, philo->eating == 0을 통해 철학자가 현재 식사를 하고 있지 않다는 상태를 체크합니다. 철학자가 이미 죽었거나 식사를 하지 않은 경우, 아래 코드가 실행됩니다.
  3. 조건을 만족할 경우:
    • 철학자가 죽을 시간(time_to_die)이 지나고, 현재 식사를 하고 있지 않으면 pthread_mutex_unlock(philo->meal_lock)을 통해 뮤텍스를 해제하고, 1을 반환하여 함수를 종료합니다. 반환 값 1은 철학자가 죽었다는 신호일 수 있습니다.
  4. 조건을 만족하지 않을 경우:
    • 철학자가 죽지 않았거나, 현재 식사를 하고 있는 경우에는 단순히 뮤텍스를 해제하고 함수가 종료됩니다.

예시:

상황 1:

  • 철학자 philo는 마지막 식사를 한 지 time_to_die 이상이 되었고, 현재 식사를 하지 않고 있습니다.
  • 이 경우 get_current_time() - philo->last_meal >= time_to_die가 참이 되어 철학자가 죽었음을 나타내기 위해 1을 반환합니다.

상황 2:

  • 철학자 philo는 마지막 식사 후 time_to_die보다 시간이 더 적고, 현재 식사를 하고 있지 않습니다.
  • 이 경우 pthread_mutex_unlock(philo->meal_lock)이 호출되고, 함수는 종료됩니다. 철학자는 아직 죽지 않았으므로, 다른 스레드가 이 객체를 수정할 수 있습니다.

결론:

이 코드는 철학자가 식사 시간 이후에 죽을 수 있는지를 확인하고, 죽을 시간이 지난 철학자는 더 이상 작업을 하지 않도록 처리하는 부분입니다. 동시에 뮤텍스를 사용하여 여러 스레드가 공유 자원에 안전하게 접근할 수 있도록 하고 있습니다.

t_philo 구조체 분석

typedef struct s_philo
{
    pthread_t        thread;              // 스레드 ID
    int              id;                  // 철학자의 ID
    int              eating;              // 철학자가 식사 중인지 여부
    int              meals_eaten;         // 철학자가 먹은 식사의 횟수
    size_t           last_meal;           // 철학자가 마지막으로 식사를 한 시간
    size_t           time_to_die;         // 죽기까지 걸리는 시간
    size_t           time_to_eat;         // 식사하는 데 걸리는 시간
    size_t           time_to_sleep;       // 잠자는 데 걸리는 시간
    size_t           start_time;          // 프로그램 시작 시간
    int              num_of_philos;       // 철학자의 수
    int              num_times_to_eat;   // 각 철학자가 먹어야 하는 식사 횟수
    int              *dead;               // 철학자가 죽었는지 여부를 나타내는 포인터
    pthread_mutex_t  *r_fork;             // 오른쪽 포크의 뮤텍스
    pthread_mutex_t  *l_fork;             // 왼쪽 포크의 뮤텍스
    pthread_mutex_t  *write_lock;         // 출력 관련 뮤텍스
    pthread_mutex_t  *dead_lock;          // 죽음 상태 관련 뮤텍스
    pthread_mutex_t  *meal_lock;          // 식사 상태를 보호하는 뮤텍스
}                   t_philo;

meal_lock의 역할

  1. 식사 상태 보호:
    • meal_lock은 철학자의 식사 상태와 관련된 중요한 데이터를 보호하는 뮤텍스입니다. 특히, last_meal 변수는 철학자가 마지막으로 식사를 한 시간 정보를 나타내는데, 이는 철학자가 죽었는지 아니면 계속 식사를 해야 하는지를 결정하는 중요한 정보입니다.
  2. last_meal 보호:
    • last_meal은 철학자가 마지막으로 식사한 시간을 기록하는 변수입니다. 이 값은 철학자가 죽었는지 여부를 판단하는 기준이 됩니다.
    • 코드에서 last_meal을 읽거나 수정하려면 동시성 문제를 피하기 위해 meal_lock을 사용하여 **상호 배제 (mutual exclusion)**를 보장합니다.
    • 예를 들어, 여러 스레드가 동시에 last_meal에 접근하여 이 값을 수정하거나 읽으면 일관성 문제가 발생할 수 있기 때문에, meal_lock을 사용해 철학자 객체를 안전하게 보호합니다.
  3. eating 변수와 meal_lock:
    • eating 변수는 철학자가 현재 식사를 하고 있는지 여부를 나타냅니다. 이 변수 또한 다른 스레드와의 충돌을 피하기 위해 보호가 필요할 수 있습니다.
    • 예를 들어, 철학자가 식사 중인지 여부에 따라 식사 시간을 관리하거나 상태 변경을 해야 할 수 있으므로, eating에 대한 변경이나 조회도 meal_lock으로 보호하는 것이 안전합니다.
  4. meal_lock과 다른 뮤텍스와의 관계:
    • meal_lock은 철학자 객체 내에서 식사와 관련된 데이터만을 보호하는 데 사용되므로, 다른 뮤텍스들과 역할이 겹치지 않습니다. 예를 들어, r_fork와 l_fork는 포크의 상태를 보호하고, write_lock은 출력을 제어하는 역할을 합니다.
    • meal_lock은 오직 식사 상태를 보호하는 전용 뮤텍스로, 다른 뮤텍스들과 협력하여 철학자의 전반적인 상태를 관리합니다.

meal_lock을 사용하는 이유

  • meal_lock은 last_meal과 eating과 같은 변수를 동기화하고 보호하는 데 필요합니다. 철학자가 식사를 할 때마다, 그 시간을 기록하거나 상태를 변경하는데 이 변수들이 여러 스레드에서 동시에 수정되면 데이터 충돌이 발생할 수 있기 때문에, meal_lock을 사용하여 해당 자원에 대한 접근을 직렬화(serializing)하는 것입니다.

예시 코드

pthread_mutex_lock(philo->meal_lock); // meal_lock을 잠근다.

if (get_current_time() - philo->last_meal >= philo->time_to_die && philo->eating == 0) {
    // 철학자가 죽었을 시간이라면 처리 (예: 죽은 상태를 반환)
    pthread_mutex_unlock(philo->meal_lock); // 뮤텍스를 해제하고 종료
    return 1;
}

pthread_mutex_unlock(philo->meal_lock); // 뮤텍스를 해제

위 코드에서 meal_lock은 철학자의 식사 시간을 보호하고 있습니다. last_meal이 마지막 식사 시간을 나타내기 때문에, 철학자가 마지막 식사를 한 시간과 죽을 시간을 비교하는 중요한 역할을 합니다. 이 값에 대한 접근을 보호하기 위해 meal_lock을 사용하여 동시성 문제를 방지하는 것입니다.


결론

meal_lock은 철학자의 식사 시간과 관련된 데이터를 보호하는 뮤텍스로, 특히 last_meal과 eating 같은 변수에 대한 동시 접근을 막고 안전한 데이터 관리를 보장합니다. meal_lock이 철학자의 식사 상태를 보호하는 역할을 하므로, 철학자들이 안전하게 식사를 기록하고 처리할 수 있도록 도와줍니다.

'C Language' 카테고리의 다른 글

뮤텍스 포인터 차이  (0) 2025.03.13
mutex 잠금 원리  (0) 2025.03.13
pthread_create pthread_join 설명  (0) 2025.03.12
pthread_mutex_lock unlock  (0) 2025.03.10
mutex null 속성으로 초기화 + fork  (0) 2025.03.10