12/30/2023

스레드 메모리 누수

발단

Valgrind를 통해 지금까지 작성한 다중스레드 프로그램을 확인하면서 이상한 점을 발견했다. 가장 기초적인 프로그램에서 조차 메모리 누수가 발생했다! 이유를 찾아 검색하던 도중 배운게 꽤 많아 이번 포스팅에서 정리하고자 한다.


결합 vs. 분리

기본적으로 pthread_create() 함수로 생성된 스레드는 결합 가능하다. 이것이 처음 작성한 다중스레드 프로그램에서 메모리 누수가 발생한 원인이었다! 스레드는 종료 시 관련 자원이 바로 해제되지 않기 때문에 부모 스레드가 pthread_join() 함수를 호출하여 자식 스레드가 종료될 때까지 대기해야 한다. 아니면, 스레드는 pthread_create() 함수에서 분리된 상태로 생성되거나 결합 가능한 상태로 생성되어도 pthread_detach() 함수로 분리된 상태로 설정되어야 한다.


pthread_join()

밑의 코드를 보면 pthread_attr() 함수가 없기 때문에 생성된 스레드는 결합 가능하다. 따라서 pthread_join() 함수를 메인 스레드에서 호출해야 한다. 가장 쉬운 방법은 당연히 바로 밑에 pthread_join() 함수를 호출하는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void
create_thr() {
  int err;
  pthread_t tid;
  static char *in = "스레드 #1";
  
  // 스레드 분기 지점.
  // 4번 인자는 반드시 힙 영역 혹은 데이터 영역 메모리.
  err = pthread_create(&tid, NULL, thr_fn, (void *)in);
  if (err != 0) {
    printf("오류 발생! 스레드 생성 실패, errno = %d\n", err);
    exit(EXIT_FAILURE);    
  }
 
  // 결합 가능한 스레드의 자원은 부모 스레드가 해당 스레드에 결합할 때까지 해제되지 않는다.
  // 분리된 스레드의 자원은 해당 스레드가 종료되는 즉시 해제된다.
  // 결합 가능한 스레드는 실행 중에 분리된 스레드로 변환할 수 있으며 반대도 마찬가지다.
  // 기본적으로 스레드는 결합 가능한 모드에서 실행된다.
  // 자원을 해제하는 세 가지 방법.
  //   1. 스레드를 분리된 속성으로 생성한다(PTHREAD_CREATE_DETACHED).
  //   2. 스레드 생성 후 분리한다(pthread_detach()).
  //   3. 종료된 스레드와 결합한다(pthread_join()).
  pthread_join(tid, NULL);
  if (err != 0) {
    printf("오류 발생! 스레드 결합 실패, errno = %d\n", err);
    exit(EXIT_FAILURE);
  }
}
 
int
main(int args, char *argv[]) {
 
  create_thr();
  printf("메인 스레드 종료.\n");
  // pause() 함수가 사용될 경우.
  // printf("메인 스레드 중지.")
 
  // 메인 스레드가 종료되어도 전체 프로세스를 종료하지 않으며 자식 스레드는 계속 실행된다.
  // create_thr() 함수에서 pthread_join() 함수가 사용될 경우 필요없다.
  pthread_exit(NULL);
  
  // pause() 함수가 주석으로 처리되면 메인 스레드 종료 시 모든 자식 스레드가 종료된다.
  // pause();
 
  // 메인 함수에서 return을 사용하거나 exit() 함수를 호출하면 프로세스를 종료시킨다.
  exit(EXIT_SUCCESS);
}
cs

update: 2024.02.14

댓글 없음:

댓글 쓰기