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
- 이 문제를 해결하기 위해 사용하는 방법 중 하나가 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를 반드시 사용해야한다.
'컴퓨터공학기초 개념 > 시스템 프로그래밍' 카테고리의 다른 글
36. 시스템 프로그래밍 - mmap 사용방법 (0) | 2021.09.27 |
---|---|
35. 시스템 프로그래밍 - 메모리 & mmap (3) | 2021.09.27 |
33. 스레드 - 이해와 기본 (0) | 2021.09.24 |
32. 쉘 스크립트 - 현업 예제(backup, log, tar, find) (0) | 2021.09.24 |
31. 쉘 스크립트 - 조건문과 반복문 (0) | 2021.09.24 |
댓글