본문 바로가기
컴퓨터공학기초 개념/시스템 프로그래밍

34. 스레드 - detach와 mutex

by devraphy 2021. 9. 25.

0. 시작하기전에

- 이전 포스팅에서 스레드에 대해서 배우면서, 스레드 관리를 위한 기본적인 함수들을 알아봤다.

- 이번 포스팅에서는 추가적인 스레드 함수와 동기화에 대해서 알아보자.  


1. pthread detach

- 스레드를 해제시키는 함수에는 2가지가 있다.

 

▶ pthread_join

   - 추가 생성된 스레드가 종료될 때까지 메인 스레드는 대기상태가 된다.

   - 추가 스레드의 작업이 종료되면 스레드 자원을 해제한다. 

 

 

▶ pthread_detach

   - pthread_join을 기다리지 않고, 호출 즉시 스레드 리소스(메모리 영역)를 해제한다.

   - 즉, 추가된 스레드의 작업을 진행하지 않고 바로 스레드 리소스를 해제하게된다. 

 

 

- 다음 함수의 원문을 보자.

// thread: detach할 스레드의 식별자
int pthread_detach(pthread_t thread);

 

 

a) 실습예제

- 우선 일반적으로 join만 있을 때를 살펴보자. 

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void *t_function(void *ptr) { // 스레드에서 작업할 함수
        char *message;
        message = (char *)ptr;
        printf("%s start \n", message);
        sleep(5);
        printf("%s end\n", message);
        return 0;
}

int main() {
        pthread_t thread1, thread2; // 스레드 변수 생성
        const char *message1 = "Thread1";
        const char *message2 = "Thread2";
        int ret, status;

        ret = pthread_create(&thead1, NULL, t_function, (void *)message1); // 스레드 생성
        if (ret < 0) {
                perror("thread1 create error");
        }
        pthread_join(thread1, (void **)&status); // 스레드1 조인
        printf("therad1 returns: %d\n", status);

        ret = pthread_create(&thread2, NULL, t_function, (void *)message2); // 스레드 생성
        if (ret < 0) {
                perror("thread2 create error");
        }

        sleep(10);
        return 0;
}

 

 

 

- 만약 스레드1이 생성된 후에 바로 detach 코드를 실행하면 어떻게 될까?

- 스레드 1을 detach하고 스레드2를 join해보자. 

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void *t_function(void *ptr) { // 스레드에서 작업할 함수
        char *message;
        message = (char *)ptr;
        printf("%s start \n", message);
        sleep(5);
        printf("%s end\n", message);
        return 0;
}

int main() {
        pthread_t thread1, thread2; // 스레드 변수 생성
        const char *message1 = "Thread1";
        const char *message2 = "Thread2";
        int ret, status;

        ret = pthread_create(&thread1, NULL, t_function, (void *)message1); // 스레드 생성
        if (ret < 0) {
                perror("thread1 create error");
        }

        pthread_detach(thread1); // detach 실행

        ret = pthread_create(&thread2, NULL, t_function, (void *)message2); // 스레드 생성
        if (ret < 0) {
                perror("thread2 create error");
        }

        pthread_join(thread2, (void **)&status);
        printf("thread2 returns: %d\n", status);

        sleep(10);
        return 0;
}

 

 

b) 실습예제 결과비교

- 이제 두가지 실습예제의 결과를 비교해보자. 

 

 

1. join만 사용한 경우

- thread1이 시작되면 5초간의 딜레이가 발생한다.

- thread1이 종료되면 thread2가 시작되고, thread2에서 10초간의 딜레이가 발생한다.

- 프로그램은 완전히 종료된다. 

 

 

2. detach를 사용한 경우

- thread2가 우선 생성되는 것은 상관없다.

- 중요한 것은 thread1이 생성되고 나서 therad1의 작업을 진행하지 않았다는 점이다.

- thread1이 detach 되었기에 바로 thread2의 작업으로 넘어간 것을 확인할 수 있다.

 

 

c) 실습예제의 결론

- detach를 호출하면 현재 진행중이던 스레드의 작업을 즉시 종료한다. 그리고 바로 다음 수행해야할 코드로 넘어간다. 

- 반대로 join은 스레드의 작업이 끝날때 까지 다음 작업을 수행하지 않고 대기한다.

- 공통점은 detach와 join 모두 각자의 기능을 다 하고 나면 메모리에서 스레드를 해제시킨다는 것이다. 

 


2. pthread mutex(상호배제 기법)

- 운영체제 파트에서 스레드 동기화 문제에 대해서 이야기한 적이 있다.

- 하나의 프로세스 내부에 존재하는 여러개의 스레드가 프로세스의 데이터를 공유하기 때문에 발생하는 문제다. 

 

https://devraphy.tistory.com/191

 

27. 스레드 동기화 이슈 해결방법

- 본 포스팅을 읽기 전에, 이전 포스팅 26.스레드 동기화를 먼저 읽는 것을 권장합니다. 1. 스레드 동기화 이슈의 원인 # 파이썬 코드입니다. import threading g_count = 0 def thread_main(): global g_count fo..

devraphy.tistory.com

  

- 이 문제를 해결하기 위해 사용하는 방법 중 하나가 Mutex(mutual exclusive) 기법이다.

 

 

a) pthread mutex

- mutex를 사용하기 위한 몇가지 함수의 원문을 살펴보자. 

// 뮤텍스 선언과 초기화
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;

// 뮤텍스 락 걸기
int pthread_mutex_lock(pthread_mutex_t *mutex);

// 뮤텍스 락 풀기
int pthread_mutex_unlock(pthread_mutex_t *mutex);

 

 

b) 실습예제

- 예제코드를 보면서 pthread mutex를 이해해보자. 

- 아래의 코드는 mutex를 사용했을 때의 코드다. 

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER; // 뮤텍스 변수 선언 및 초기화
int g_count = 0;

void *t_function(void *data) { // 스레드에서 수행할 함수
        char * thread_name = (char*)data;
        pthread_mutex_lock(&mutex_lock); // mutex lock
        printf("%s start\n", thread_name);
        for (int i = 0; i < 10000000; i++) {
                g_count++;
        }
        printf("%s, g_count = %d\n", thread_name, g_count);
        pthread_mutex_unlock(&mutex_lock); // mutex unlock
}

int main() {
        pthread_t p_thread1, p_thread2;
        int status;
        pthread_create(&p_thread1, NULL, t_function, (void *)"Thread1");
        pthread_create(&p_thread2, NULL, t_function, (void *)"Thread2");
        pthread_join(p_thread1, (void *)&status);
        pthread_join(p_thread2, (void *)&status);
}

- pthread_mutex를 사용했기에 동기적으로 실행된 것을 확인할 수 있다.

- 첫번째로 실행된 스레드(thread2)에서 g_count를 천만까지 연산했다.

- 이어서 두번째로 실행된 스레드(thread1)에서 g_count를 추가로 천만까지 연산하여 g_count는 총 2천만이 되었다. 

 

 

- 만약 mutex를 사용하지 않는다면 어떻게 될까? 

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_count = 0;

void *t_function(void *data) { // 스레드에서 수행할 함수
        char * thread_name = (char*)data;
        printf("%s start\n", thread_name);
        for (int i = 0; i < 10000000; i++) {
                g_count++;
        }
        printf("%s, g_count = %d\n", thread_name, g_count);
}

int main() {
        pthread_t p_thread1, p_thread2;
        int status;
        pthread_create(&p_thread1, NULL, t_function, (void *)"Thread1");
        pthread_create(&p_thread2, NULL, t_function, (void *)"Thread2");
        pthread_join(p_thread1, (void *)&status);
        pthread_join(p_thread2, (void *)&status);
}

- 스레드는 프로세스 내부의 데이터를 공유하기 때문에 비동기적 실행을 하면 위의 결과처럼 엉망이된다.

- 더불어, 프로그램을 돌릴 때마다 다른 결과값이 나온다.

- 이와 같이, 스레드는 동기화 문제를 갖고 있으며 이를 해결하기 위해서 mutex를 반드시 사용해야한다. 

댓글