
저자: Michael Kerrisk
이 책의 소스 코드
-- 스레드 위주로
[Vol.2]
1장 스레드: 소개
2장 스레드: 스레드 동기화
3장 스레드: 스레드 안전성과 스레드별 저장소
4장 스레드: 스레드 취소
5장 스레드: 기타 세부사항
-- 뮤텍스 & 세마포
[Vol.2]
6장 프로세스 간 통신 개요 -> 뮤텍스
16장 POSIX 세마포어
프로그램에서의 시간
1) 실제 시간: 어떤 표준 시점을 기준으로 측정(달력 시간)되거나 프로세스 동작 중 어떤 고정된 시점(프로그램의 시작)을 기준으로 측정된 시간(경과 시간). -> 파일의 타임스탬프
2) 프로세스 시간: 프로세스가 사용한 CPU 시간의 양 -> 알고리즘 성능 검사/최적화
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
# 성공하면 0을 리턴하고, 에러가 발생하면 -1 리턴
struct timeval
{
time_t tv_sec; /* UTC 1970년 1월 1일 00:00:00 이래의 초 */
suseconds_t tv_usec; /* 추가적인 마이크로초(long int) */
};
현재 사용 컴퓨터에서는 얼마나 정확할까
| 데이터형 | 설명 | 사용 목적 | 예시 |
|---|---|---|---|
pthread_t | 스레드를 식별하기 위한 ID (추상적 핸들, 실제 내부 구현은 구현체에 따라 다름) | pthread_create()로 생성한 스레드를 관리하거나 pthread_join()으로 대기할 때 사용 | c pthread_t tid; pthread_create(&tid, NULL, threadFunc, NULL); |
pthread_attr_t | 스레드 속성 객체. 스택 크기, detach 상태 등 스레드 생성 시 옵션을 지정 | pthread_attr_init() 후 속성 설정, pthread_create()에 전달 | c pthread_attr_t attr; pthread_attr_init(&attr); |
pthread_mutex_t | 상호 배제를 위한 뮤텍스(mutex) 객체 | 임계 구역 보호, 경쟁 상태 방지 | c pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutexattr_t | 뮤텍스 속성 객체 | 뮤텍스의 동작 모드(예: 재귀적 잠금 가능 여부) 설정 | c pthread_mutexattr_t mattr; |
pthread_cond_t | 조건 변수(Condition Variable) | 스레드 간 신호 전달, 특정 조건 충족까지 대기 | c pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
pthread_condattr_t | 조건 변수 속성 객체 | 조건 변수의 동작 특성 지정 | c pthread_condattr_t cattr; |
pthread_rwlock_t | 읽기-쓰기 락(Read-Write Lock) | 다수의 리더, 단일 라이터 동기화 제어 | c pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; |
pthread_rwlockattr_t | RW락 속성 객체 | 읽기-쓰기 락의 속성 지정 | c pthread_rwlockattr_t rwattr; |
pthread_barrier_t | 배리어(Barrier) 객체 | 여러 스레드가 특정 지점까지 도달할 때까지 동기화 | c pthread_barrier_t barrier; |
pthread_barrierattr_t | 배리어 속성 객체 | 배리어 동작 특성 지정 | c pthread_barrierattr_t battr; |
pthread_key_t | 스레드별 데이터(Thread-Specific Data, TLS) 키 | 각 스레드 고유의 데이터를 저장하고 접근 | c pthread_key_t key; pthread_key_create(&key, destructor); |
pthread_once_t | 초기화를 한 번만 수행하도록 보장하는 객체 | 전역 자원 초기화 시 사용 | c pthread_once_t once = PTHREAD_ONCE_INIT; |
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start) (void *), void *arg);
# 성공하면 0 리턴, 에러 발생하면 에러 번호(양수) 리턴
프로그램이 시작될 때, 프로세스는 초기(initial) 또는 주(main) 스레드라는 하나의 스레드로 이뤄져 있다.
새 스레드는 start로 지정된 함수를 인자 arg로 호출해 시작한다.(start(arg))
예시
#include <pthread.h>
#include <stdio.h>
void *worker(void *arg)
{
char *msg = (char *)arg; // 전달받은 인자 해석
printf("스레드에서 받은 메시지: %s\n", msg);
return NULL;
}
int main()
{
pthread_t t1;
char *text = "안녕하세요, 스레드!";
// 스레드를 생성하면서 worker 함수를 실행하라고 지정
pthread_create(&t1, NULL, worker, text);
pthread_join(t1, NULL); // 스레드 종료 대기
return 0;
}
프로세스 내의 각 스레드는 고유한 스레드 ID를 갖는다. 이 ID는 pthread_create()를 호출한 스레드에 리턴되고, 스스로의 ID는 pthread_self()를 통해 얻을 수 있다.
#include <pthread.h>
pthread_t pthread_self(void);
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
# 성공하면 0 리턴, 에러 발생하면 에러 번호(양수) 리턴
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
# 성공하면 0을 리턴하고, 에러가 발생하면 에러 번호(양수) 리턴
int pthread_mutex_lock(pthread_mutex_t *mutex);
# 성공하면 0 리턴, 에러 발생하면 에러 번호(양수) 리턴
흥미로운 질문
책에서 pthread_mutex, POSIX semaphores, fnctl(파일 레벨 락킹) 3개의 성능을 초로 비교하고 있다.
아래는 책 내용과 gpt를 동료삼아 정리한 내용이다.
1) 뮤텍스(pthread_mutex)
2) 세마포어(POSIX semaphores)
3) fcntl(파일 레벨 락킹)
속도(빠름 ←→ 느림)
뮤텍스 |█████████████████████████████
세마포어 |█████████
fcntl |██
4) 참고
리눅스에서 뮤텍스는 퓨텍스(futex, fast user space mutex)로 구현되어 있다.
deadlock: 영원히 기다림 상태에 빠지는 현상
- 잠금을 걸고 풀지 않거나, 스레드들이 서로의 잠금을 기다리면 데드락 발생
여기 부분은 하면서 계속 수정될 수 있음.
1) 뮤텍스 서열 정의
2) 시도한 다음 물러나기
3) 공정성 문제를 보통 어떻게 해결하나?
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
PTHREAD_MUTEX_INITIALIZER는 정적으로 할당된 뮤텍스를 기본 속성으로 초기화 할 때만 쓸 수 있다. 다른 모든 경우에는 pthread_mutex_init()를 써서 뮤텍스를 동적으로 초기화해야 한다.
뮤텍스의 동작
1) 하나의 스레드는 같은 뮤텍스를 두 번 잠그면 안 된다.
2) 스레드는 자신이 소유하지 않은 뮤텍스를 풀면 안 된다.
3) 스레드는 현재 잠겨 있지 않은 뮤텍스를 풀면 안 된다
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
// 성공하면 0을 리턴, 에러가 발생하면 에러 번호(양수) 리턴