
Thread-safe vs Reentrant 정리
Thread-safe : 여러 쓰레드가 동시에 호출해도 안전함 (주로 Lock/Mutex 사용)
Reentrant : 실행 도중 중단되었다가 다시 호출되어도 안전함 (공유 상태 사용 금지)
- 일반적으로 Reentrant 함수는 Thread-safe하다. 그러나 Thread-safe 함수가 반드시 Reentrant인 것은 아니다.
2. Thread-safe
여러 스레드가 동시에 실행해도 데이터 레이스가 발생하지 않도록 보호된 함수.
예시:
int g_cnt = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void thread_safe_func() {
pthread_mutex_lock(&lock);
g_cnt++;
pthread_mutex_unlock(&lock);
}
특징:
- 공유 자원 접근 시 Lock 사용 가능
- 동시 실행은 안전
- 인터럽트 중 재진입 시 Deadlock 가능
3. Reentrant
함수가 실행 도중 인터럽트되거나 시그널 핸들러에서 다시 호출되어도 안전한 함수.
조건:
- 전역 변수 사용 금지
- static 변수 사용 금지
- Lock 사용 금지
- 모든 상태는 호출자가 제공
예시:
void reentrant_func(int *counter) {
(*counter)++;
}
특징:
- 오직 인자와 지역 변수만 사용
- 중단 후 재진입 가능
- 인터럽트 환경에서도 안전
4. 대표적인 예시: strtok vs strtok_r
4-1) strtok (Non-reentrant, Not Thread-safe)
- 내부 static 변수를 사용
- 여러 스레드에서 동시에 사용하면 충돌 발생
4-2) strtok_r (Reentrant)
- 상태 저장용 포인터를 호출자가 직접 관리
- 스택 기반 상태 관리
- Thread-safe 및 Reentrant
5. Async-Signal-Safe 개념
시그널 핸들러 내부에서 호출 가능한 함수 집합.
중요:
- printf, malloc, free 등은 대부분 Async-Signal-Safe가 아님
- signal handler 안에서 호출하면 Undefined Behavior 가능
- POSIX에서 안전하다고 명시한 함수만 사용해야 함 (예: write, _exit 등)
결론
1. Thread-safe: "동시에 여러 명이 써도 되는가?"
→ YES (Mutex 써서 줄 세워도 됨).
2. Reentrant: "쓰다가 중간에 멈추고, 다시 처음부터 실행해도 되는가?"
→ YES (Mutex 쓰면 안 됨, 오직 Stack만 사용).
3. 임베디드/시스템 개발자라면
- 인터럽트 핸들러나 시그널 핸들러 내부에서는 반드시 Reentrant 함수(Async-Signal-Safe)만 호출해야 한다.
- printf, malloc 등은 대부분 Non-reentrant이므로 사용하면 안 된다.
- 멀티스레드 환경에서는 Thread-safe만으로 충분할 수 있지만, 인터럽트/시그널 환경에서는 Reentrant + Async-Signal-Safe가 필수이다.
Lock, Mutex 그리고 Semaphore의 차이가 무엇이고 언제 써야 하는건가요?