| 노션이 보기 편합니다
노션
<쓰레드설명, 쓰레드동기화 방법의 종류>
뮤텍스(Mutex)와 세마포어(Semaphore)의 차이
<뮤텍스와 세마포어의 차이>
[10분 테코톡] 🎲 와일더의 Mutex vs Semaphore
<우테코 뮤텍스 세마포어 설명>
각각의 쓰레드는 위 그림과 같이 각자의 스택과 PC 레지스터 값을 가진다.
스택 메모리 공간이 독립적이라는 것은,
을 뜻한다.
PC 레지스터란, 쓰레드가 명령어의 어디까지 수행하였는지를 나타낸다.
쓰레드는 CPU를 할당받았다가 스케줄러에 의해 다시 선점당한다.
즉, 명령어를 연속적으로 수행하지 않고 중간중간 대기 시간이 존재한다는 것이다.
각 쓰레드가 어느 부분까지 수행했는지를 기억하기 위해 PC 레지스터를 독립할당한다.
기타 각 방법의 설명과 차이는 아래 블로그 참고
뮤텍스(Mutex)와 세마포어(Semaphore)의 차이
💡 POSIX Thread의 약자로, 유닉스 계열 POSIX시스템에서 병렬적으로 작동하는 소프트웨어를 작성하기 위해 제공하는 API.
(쓰레드를 쉽게 만들기 위해 도와주는 API)
💡 pthread_create(pthread_t thread, const pthread_attr_t attr, void (start_routine)(void ), void arg);특정 함수를 실행하는 쓰레드를 만드는 함수
1번 매개변수(pthread_t *thread
)
2번 매개변수(const pthread_attr_t *attr
)
3번 매개변수(void *(*start_routine)(void *)
)
4번 매개변수(void *arg
)
💡 int pthread_join(pthread_t th, void **thread_return)
th쓰레드가 종료되길 기다렸다가, 쓰레드가 종료되면 다음 명령어 진행
1번 매개변수(pthread_t th
)
2번 매개변수(void **thread_return)
💡 int pthread_detach(pthread_t th);
join과 달리 반환값 처리가 불가하다.
1번 매개변수(pthread_t th
)
💡 join → blocking function
detach → non-blocking function
detach로 실행할 경우 main thread가 쓰레드 생성 후 약 1초 뒤 종료가 되기에, 결과를 의도할 수 없다.
따라서, join함수를 사용할 것 같다.
void *p_func(void *data)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
char *thread_name = (char *)data;
int i = 0;
while (i < 3)
{
printf("tn : %s, tid : %x, pid : %u\n", thread_name, (unsigned int)tid, (unsigned int)pid);
i++;
sleep(1);
}
return (0);
}
int main()
{
.
.
.
.
thr_id = pthread_create(&pthread[0], NULL, p_func, (void*)p1); //2
if(thr_id < 0)
{
perror("pthread0 create error");
exit(1);
}
thr_id = pthread_create(&pthread[1], NULL, p_func, (void *)p2); //2
if(thr_id < 0)
{
perror("pthread1 create error");
exit(1);
}
// pthread_join(pthread[0], (void **)&status); //6
// pthread_join(pthread[1], (void **)&status);
pthread_detach(pthread[0]);
pthread_detach(pthread[1]);
sleep(1);
printf("end??\n");
return 0;
}
두 쓰레드가 충돌하지 못하게 예방하는 신호등을 Mutex라고 한다.
💡 pthread_mutex_init(pthread_mutex_t mutex, const pthread_mutex_attr attr)
1번 매개변수(pthread_mutex_t *mutex
)
2번 매개변수(const pthread_mutex_attr *attr
)
💡 int pthread_mutex_destroy(pthread_mutex_t *mutex)
1번 매개변수(pthread_mutex_t *mutex
)
자원을 되돌려준다.
💡 int pthread_mutex_lock(pthread_mutex_t *mutex)
1번 매개변수(pthread_mutex_t *mutex
)
해당 뮤텍스에 대해 잠금을 시도하는데, 이미 다른 쓰레드에 의해 잠겨있다면 잠금을 얻을 수 있을 때까지 무한대기.
💡 int pthread_mutex_unlock(pthread_mutex_t *mutex);
1번 매개변수(pthread_mutex_t *mutex
)
뮤택스 lock / unlock은 중요 프로세스를 제외하곤 미표기하였음
포크 집는 순서를 조정하여 데드락 확률을 반으로 줄일 수 있다.
홀수 번째 철학자와 짝수 번째 철학자의 시간 term을 활용하여 데드락 확률을 줄일 수 있다.
쓰레드 생성 후에도, 나머지 쓰레드의 생성까지 대기하여 다수의 철학자의 식사 시작 시간을 최대한 비슷하게 할 수 있다.
start_flag mutex
를 통해 쓰레드 생성 후 대기할 수 있다.static void *philo_life_cycle(void *v)
{
t_philo *philo;
philo = (t_philo *)v;
check(pthread_mutex_lock(&philo->var->start_flag), MUTEX_LOCK);
check(pthread_mutex_unlock(&philo->var->start_flag), MUTEX_UNLOCK);
waiting(philo);
while (1)
{
...
}
return (0);
}
static int philosophere(int argc, char *argv[])
{
...
check(pthread_mutex_lock(&var.start_flag), MUTEX_LOCK);
while (i < var.philo_count)
{
check(pthread_create(&(var.philo[i].thread), 0,
philo_life_cycle, &var.philo[i]), THREAD_CREATE);
if (!var.philo[i++].thread)
ft_error("thread create error");
}
check(gettimeofday(&var.start_time, 0), GET_TIME);
check(pthread_mutex_unlock(&var.start_flag), MUTEX_UNLOCK);
...
}
sleep까지 수행한 철학자(쓰레드)가 양 옆의 철학자가 포크를 집기 전 다시 본인이 포크를 선점하는 것을 방지하면, 기아 발생을 현저히 줄일 수 있다.
sleep함수를 쪼개어 수시로 쓰레드가 깨어날 수 있게 한다.