본문 바로가기

C Language

thread create 함수 설명

`thread_create` 함수는 **철학자 스레드와 모니터 스레드를 생성하고 관리**하는 핵심 함수입니다. 단계별로 상세히 설명드리겠습니다:

---

### **1. 함수 구조 및 매개변수**
```c
int thread_create(t_center *center, pthread_mutex_t *forks)
```
- **`t_center *center`**: 철학자, 뮤텍스, 공유 데이터를 포함하는 중앙 제어 구조체입니다.
- **`pthread_mutex_t *forks`**: 철학자들이 사용하는 포크(뮤텍스) 배열입니다.

---

### **2. 주요 동작 단계**
#### **(1) 모니터 스레드 생성**
```c
pthread_t observer;
if (pthread_create(&observer, NULL, &monitor, center->philos) != 0)
    all_free("Thread creation error", center, forks);
```
- **목적**: 모든 철학자의 상태를 감시하는 별도 스레드를 생성합니다.
- **동작**:
  - `monitor` 함수를 실행하는 `observer` 스레드를 시작합니다.
  - 실패 시 `all_free`로 자원을 정리하고 오류 메시지를 출력합니다.

#### **(2) 철학자 스레드 생성**
```c
i = 0;
while (i philos[0].total_philos) {
    if (pthread_create(&center->philos[i].thread, NULL, &philo_routine, &center->philos[i]) != 0)
        all_free("Thread creation error", center, forks);
    i++;
}
```
- **목적**: 각 철학자에게 독립적인 스레드를 할당합니다.
- **동작**:
  - 철학자 수(`total_philos`)만큼 루프를 실행합니다.
  - 각 철학자 스레드는 `philo_routine` 함수를 실행합니다.
  - 실패 시 즉시 자원 정리 및 종료합니다.

#### **(3) 모니터 스레드 종료 대기**
```c
i = 0;
if (pthread_join(observer, NULL) != 0)
    all_free("Thread join error", center, forks);
```
- **목적**: 모니터 스레드가 완료될 때까지 대기합니다.
- **중요성**: 모니터가 철학자들의 종료 조건(사망 또는 식사 완료)을 감지하면 `observer` 스레드가 먼저 종료됩니다.

#### **(4) 철학자 스레드 종료 대기**
```c
while (i philos[0].total_philos) {
    if (pthread_join(center->philos[i].thread, NULL) != 0)
        all_free("Thread join error", center, forks);
    i++;
}
```
- **목적**: 모든 철학자 스레드가 정상 종료될 때까지 대기합니다.
- **동작**:
  - 각 철학자 스레드의 종료를 순차적으로 기다립니다.
  - 오류 발생 시 자원 정리 후 종료합니다.

---

### **3. 동작 흐름 도식화**
```mermaid
sequenceDiagram
    participant Main
    participant Observer
    participant Philosopher1
    participant PhilosopherN

    Main->>Observer: pthread_create(monitor)
    Main->>Philosopher1: pthread_create(philo_routine)
    Main->>PhilosopherN: pthread_create(philo_routine)
    Main->>Observer: pthread_join()
    Observer-->>Main: 모니터 종료
    Main->>Philosopher1: pthread_join()
    Philosopher1-->>Main: 철학자1 종료
    Main->>PhilosopherN: pthread_join()
    PhilosopherN-->>Main: 철학자N 종료
```

---

### **4. 주요 특징**
1. **에러 핸들링**: 모든 `pthread_create`와 `pthread_join` 호출에 대해 오류 체크를 수행합니다.
2. **자원 관리**: 오류 발생 시 `all_free` 함수를 호출하여 뮤텍스, 메모리 등을 정리합니다.
3. **순차적 실행**: 
   - 모니터 스레드가 먼저 종료된 후 철학자 스레드가 종료됩니다.
   - 이는 모니터가 종료 조건을 감지하면 철학자 스레드들이 자동으로 중단되도록 설계되었기 때문입니다.

---

### **5. 사용된 함수 설명**
| 함수 | 설명 |
|------|------|
| `pthread_create` | 새로운 스레드를 생성하고 지정된 함수를 실행합니다. |
| `pthread_join`   | 대상 스레드가 종료될 때까지 호출 스레드를 대기시킵니다. |
| `all_free`       | 오류 발생 시 할당된 메모리와 뮤텍스를 정리하는 사용자 정의 함수입니다. |

---

### **6. 중요 변수**
- **`observer`**: 모든 철학자의 상태를 모니터링하는 스레드입니다.
- **`center->philos[i].thread`**: 각 철학자의 동작을 관리하는 스레드입니다.
- **`center->philos.total_philos`**: 전체 철학자 수를 나타내는 공유 변수입니다.

---

### **7. 주의사항**
- **스레드 안정성**: 공유 자원(`dead_flag`, `last_meal` 등) 접근 시 반드시 뮤텍스 락을 사용해야 합니다.
- **데드락 방지**: 포크 획득 순서를 홀수/짝수 철학자마다 다르게 설정해야 합니다.

이 함수는 철학자 문제의 핵심 동시성 로직을 안정적으로 관리하기 위해 설계되었습니다.

`&center->philos[i]`와 `center->philos`를 다르게 받는 이유는 함수에 전달되는 데이터의 **범위**와 **구체성** 때문입니다. 이를 상세히 설명하겠습니다.

---

### **1. `&center->philos[i]`의 의미**
- **구체적인 철학자 하나를 가리킴**:  
  `&center->philos[i]`는 철학자 배열 `philos`의 `i`번째 요소(즉, 특정 철학자 구조체)의 주소를 전달합니다.
- **용도**:  
  - 개별 철학자 스레드에서 해당 철학자(`i`)만의 데이터를 처리하기 위해 사용됩니다.
  - 예를 들어, 철학자가 자신의 포크를 잡고 먹는 동작은 각 철학자 스레드에서 독립적으로 수행되기 때문에, 특정 철학자의 데이터만 필요합니다.

#### 예시:
```c
pthread_create(&center->philos[i].thread, NULL, &philo_routine, &center->philos[i]);
```
- 여기서 `&center->philos[i]`는 `philo_routine` 함수에 전달되어 해당 철학자의 행동(먹기, 생각하기 등)을 수행합니다.

---

### **2. `center->philos`의 의미**
- **철학자 배열 전체를 가리킴**:  
  `center->philos`는 철학자 배열 전체(즉, 모든 철학자의 데이터를 포함)를 가리킵니다.
- **용도**:  
  - 모니터링 스레드에서 모든 철학자의 상태를 확인하거나 관리하기 위해 사용됩니다.
  - 예를 들어, 데드락(교착 상태) 감지나 모든 철학자가 식사를 완료했는지 확인하는 작업은 모든 철학자의 데이터를 확인해야 하므로 배열 전체가 필요합니다.

#### 예시:
```c
pthread_create(&observer, NULL, &monitor, center->philos);
```
- 여기서 `center->philos`는 모니터링 함수인 `monitor`로 전달되어 모든 철학자의 상태를 확인하고 종료 조건을 판단합니다.

---

### **3. 왜 다르게 받는가?**
#### **개별 작업 vs 전체 작업**
- **개별 작업 (`&center->philos[i]`)**:  
  각 철학자는 자신의 행동만 처리하면 되므로 특정 철학자 구조체만 전달합니다.
  - 예: 포크 잡기, 먹기, 생각하기 등.
- **전체 작업 (`center->philos`)**:  
  모니터링 작업은 모든 철학자의 상태를 확인해야 하므로 배열 전체를 전달합니다.
  - 예: 데드락 감지, 모든 철학자가 식사 완료 여부 확인 등.

#### **메모리 효율성**
- 개별 작업에서는 필요한 데이터만 전달하여 메모리를 절약하고 함수 호출 오버헤드를 줄입니다.
- 전체 작업에서는 배열 전체를 참조하여 반복적으로 데이터를 처리할 수 있습니다.

---

### **4. 정리**
| 전달 방식             | 의미                                      | 용도                                      |
|-----------------------|-------------------------------------------|-------------------------------------------|
| `&center->philos[i]` | 특정 철학자 구조체의 주소                 | 개별 스레드에서 특정 철학자의 행동 처리   |
| `center->philos`     | 모든 철학자를 포함하는 배열의 시작 주소    | 모니터링 스레드에서 전체 상태 확인 및 관리 |

이처럼 두 방식은 함수가 처리해야 할 데이터 범위(개별 vs 전체)에 따라 적절히 선택됩니다.

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

ft_usleep vs usleep  (0) 2025.03.15
포크락 수정  (0) 2025.03.15
철학자의 포크 할당 로직 설명  (0) 2025.03.14
공유 뮤텍스를 사용하는 이유와 예시  (0) 2025.03.14
mutex 입금문제  (2) 2025.03.13