4-7 Thread (학습)

do·2022년 5월 11일
0

API

목록 보기
32/42

Thread

  • 하나의 프로세스 내에서 여러 개의 실행 흐름(단일, 동시적, 병렬적)을 두어 작업을 효율적으로 처리하기 위한 모델이다.
  • 사용 이유
    • 메모리 절약
    • 멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행하게 되면, 프로세스를 생성하여 자원을 할당하는 과정도 줄어들 뿐더러, 프로세스를 컨텍스트 스위치 하는 것보다 오버헤드를 더 줄일 수 있게 된다.
      • 컨텍스트 스위치
        • 스케쥴러가 기존 실행 프로세스를 우선순위 때문에 미루고, CPU에서 여러 프로세스를 돌아가면서 작업을 처리하는 과정
        • 동작 중인 프로세스가 대기하며 해당 프로세스의 상태(Context)를 보관하고, 대기하고 있던 다음 순서의 프로세스가 동작하면서, 이전에 보관했던 프로세스의 상태를 복구하는 작업
      • 오버헤드
        • 간접적인 처리 시간 및 메모리

Process vs. Thread

  • Thread를 이용하면 fork()를 이용한 프로세스 기반의 병렬처리의 문제점의 많은 부분을 해결할 수 있다.
  • Thread는 새로운 프로세스를 생성시키지 않고, 특정 문맥(코드)만을 병렬로 실행할 수 있다.
  • 새로운 프로세스를 생성시키지 않기 때문에 그만큼 자원을 아낄 수 있으며, 더 효율적으로 빠르게 움직인다.
  • 또한, 같은 프로세스이기 때문에, 데이터를 공유하기 쉽다는 장점도 가진다.

multiProcess

  • 장점: 여러 개의 자식 프로세스 중 하나에 문제가 발생하면, 그 자식 프로세스만 죽는 것 이상으로 다른 영향이 확산되지 않는다.
  • 단점:
    • 컨텍스트 스위칭 과정에서 캐쉬 메모리 초기화 등 무거운 작업이 진행되고 많은 시간이 소모되는 등의 오버헤드가 발생한다.
    • 프로세스 사이에서 공유하는 메모리가 없어 컨텍스트 스위칭이 발생하면 캐쉬에 있는 모든 데이터를 리셋하고 다시 캐쉬 정보를 불러와야 한다.

multiThread

  • 장점:
    • 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리할 수 있다.
    • 프로세스 간의 통신보다 쓰레드 간 통신 비용이 적다.
    • 쓰레드 사이의 작업량이 적어 컨텍스트 스위칭이 빠르다.
    • 쓰레드는 프로세스 내의 Stack 영역을 제외한 모든 메모리를 공유하기 때문에 통신 부담이 적다.
  • 단점:
    • 주의깊은 설계가 필요하다. (미묘한 시간, 잘못된 변수 공유로 오류 발생 확률 증가)
    • 단일 프로세스 시스템의 경우, 효과를 기대하기 어렵다.
    • 자원 공유의 문제가 발생한다. (동기화 문제)
    • 하나의 쓰레드에 문제가 발생하면, 전체 프로세스가 영향을 받는다.
      • (예시) 쓰레드 하나가 다른 프로세스의 메모리 영역을 침범할 경우 프로세스 자체가 죽어버림으로써, 프로세스에 생성된 다른 모든 쓰레드도 프로세스와 함께 죽어버린다. 이 문제는 시그널을 잘 활용하면 된다.

다른점

  • join과 detach의 차이
    • join : joinable 쓰레드의 종료까지 대기하고, 자원을 회수할 때 이용됨
      • 해당 쓰레드가 반환하는 값을 받아 활용가능
    • detach : detached 쓰레드의 종료를 대기하지 않고, 자원 즉시 반환함
  • exit과 cancel의 차이
    • exit : 즉시 종료
    • cancel : 취소 요청(쓰레드의 설정 값에 따라 가능 여부가 갈림)

1. pthread_create()

함수 원형. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
기능. 쓰레드를 생성한다.
헤더. <pthread.h>
매개변수1. pthread_t *thread 쓰레드가 성공적으로 생성되었을 때, 넘겨주는 쓰레드 식별번호
매개변수2. const pthread_attr_t *attr 쓰레드 속성 타입 (기본 NULL)
매개변수3. void *(*start_routine) 분기시켜서 실행할 쓰레드 함수
매개변수4. void *arg 위 start_routine 쓰레드 함수를 실행시킬 때 넘겨줄 인자
리턴값. 0 성공, errno 에러

  • (1) Joinable 상태 또는 Detached 상태로 변경 가능
    • int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
    • int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
pthread_attr_t attr;
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); //JOINABLE 상태로 변경
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //DETACHED 상태로 변경
  • (2) 유저 모드 또는 커널 모드로 변경 가능
    • int pthread_attr_setscope(pthread_attr_t *attr, int scope);
    • int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
    • 유저 모드 - 각각의 스레드가 시스템 자원을 배정받음. 프로세스 내부의 스레드들이 커널 스케줄러를 통해 스케줄됨. 리눅스 상에선 이 모드만 제공함
    • 커널 모드 - 프로세스에 배정된 자원을 프로세스 내의 스레드가 나눠가짐
  • (3) 스레드 스택 크기 설정 가능
    • int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
    • int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
    • 리눅스 환경의 경우, 프로세스당 최대 스택 크기는 10MB임
    • pthread로 생성된 스레드의 최대 스택 크기는 프로세스와 같음
      • 작은 메모리를 필요로 하는 스레드를 많이 생성할 경우 메모리 낭비가 생길 수 있음

2. pthread_join()

함수 원형. int pthread_join(pthread_t thread, void **return_val)
기능. 특정 쓰레드의 종료를 기다린다.
join된 쓰레드(종료된 쓰레드)는 모든 자원을 반납하게 된다.
헤더. <pthread.h>
매개변수1. pthread_t thread 기다릴 쓰레드의 식별자
매개변수2. void **return_val 쓰레드의 리턴값. NULL이 아닌 경우, pthread_exit() 함수를 통해 설정한 값을 확인할 수 있다.
리턴값. 0 성공, errno 에러

  • pthread_join()은 반드시 joinable한 상태로 생성된 쓰레드만을 기다릴 수 있다.
  • 다만 쓰레드가 detach 없이 종료되면 쓰레드 스택은 해제되지만, 쓰레드 ID와 종료 상태를 포함한 몇몇 자원들은 pthread_join()이나 pthread_detach()에 의해 해제될 때까지 남아있게 된다.
  • 종료될 때까지 기다렸다가 종료시점이 되면, 자원이 반납된다.

3. pthread_detach()

함수 원형. int pthread_detach(pthread_t thread)
기능. 쓰레드가 종료하면, 할당된 모든 자원이 즉시 반환된다. (반면, 쓰레드의 종료 상태를 알 수 없다.)
헤더. <pthread.h>
매개변수1. pthread_t thread 분리시킬 쓰레드의 식별자
리턴값. 0 성공, errno 에러

  • 쓰레드의 종료 상태를 알아내는게 중요하다면, 종료 상태를 저장할 전역변수에 종료 상태를 기록하면 된다. 쓰레드가 종료할 때 변수의 값을 바꾸고, 메인 쓰레드에 시그널을 전송하는 방법이다.

4. pthread_exit()

함수 원형. void pthread_exit(void *return_val)
기능. 스레드 종료
헤더. <pthread.h>
매개변수. void *return_val pthread_join()을 호출하는 다른 쓰레드에서 받아서 사용할 수 있다.

5. pthread_self()

함수 원형. pthread_t pthread_self(void)
기능. pthread_t 구조체는 현재 쓰레드의 식별자 정보를 담고있다. 따라서 현재 쓰레드 식별자를 확인할 수 있다.
헤더. <pthread.h>
리턴값. pthread_t 현재 쓰레드의 식별자 정보

6. pthread_cancel()

함수 원형. int pthread_cancel(pthread_t thread)
기능. 쓰레드에 취소 요청을 한다. (강제로 종료되지는 않음)
취소 요청을 받아서 종료하는 쓰레드는 pthread_exit(PTHREAD_CANCELED)을 호출하고 종료한다. 취소가 통보된 쓰레드는 쓰레드 취소 상태의 설정에 따라서 취소 요청을 무시할 수도, 취소 지점까지 수행한 뒤에 종료될 수도 있다. (취소 상태는 pthread_setcancelstate(int state, int *oldstate)함수에 의해 결정된다.)'
헤더. <pthread.h>
매개변수. pthread_t thread 인자로 주어진 쓰레드를 중지시킨다.
리턴값. 0 성공, 0이 아닌 수 에러
작업 중간에 취소 요청을 받았다면, 쓰레드는 마지막까지 진행할 수 없기 때문에, 이 경우를 대비해서 pthread_cleanup_push() pthread_cleanup_pop()과 같은 함수를 제공한다. (종료할 때 호출해야할 함수)

0개의 댓글