2014. 6. 13. 15:39

[Linux Programming] 스레드만 종료 / pthread_cleanup_push, pthread_cleanup_pop으로 마무리 처리부 등록하기


* 프로세스에서 스레드만 종료시키고자 하는 경우

  1. 스레드 함수에서 return

  2. 스레드에서 pthread_exit 호출

  3.  다른 스레드에서 pthread_cancel 호출



* pthread_cancel

한 스레드가 같은 프로세스에 속한 다른 스레드의 취소를 요청할 수 있다.


#include <pthread.h>

int pthread_cancel(pthread_t tid);   

>> 인자

첫번째 : 스레드 ID

반환값 : 성공시 0, 실패시 오류번호





* 스레드 종료시 특정 함수들이 호출되도록 설정

  - 스레드 마무리 처리부, 

    스레드 마무리 처리부들은 한 스택에 등록되는데, 

    이는 마무리 처리부들이 등록된 순서의 역순을 호출된다는 의미(선입후출)


#include <pthrad.h>

void pthread_cleanup_push(void(*rtn)(void *), void *arg);

첫번째 인수 : rtn으로 지정된 마무리 함수를 스택에 등록

두번째 인수 : 마무리 처리부 함수의 인수


void pthread_cleanup_pop(int execute);

첫번째 인수 : 0이 아닌 값을 호출하면 마무리처리부에서 pop을 수행한다.



마무리 처리부가 호출되는 상황

* 스레드가 pthread_exit를 호출했다.

* 스레드가 취소 요청에 반응했다. (다른 스레드에서 pthread_cancel을 호출한 경우)

* 스레드가 execute인수에 0이 아닌 값을 넣어서 pthread_cleanup_pop 함수를 호출했다.



* 이 두 함수에 제약

** 이들은 매크로로 구현 될 수 있기 때문에 둘의 호출은 반드시 한 스레드의 같은 범위 안에서 짝을 이루어야 한다.) 

** pthread_cleanup_push, pthread_cleanup_pop은 셋트로 써야 한다.

**** 안그러면 컴파일 시 에러가 난다.

**** 짝을 맞춰주기 위해 pop(0) 방식으로 쓰기도 함

void *thr_fn1(void *arg)

{

pthread_cleanup_push( cleanup, NULL);

pthread_cleanup_pop(0);

       pthread_exit((void*)2);

}




예제 프로그램

#include "apue.h"

#include <pthread.h>


void cleanup(void *arg)

{

    printf("cleanup : %s\n", (char *)arg);

}


void * thr_fn1(void *arg)

{

    printf("thread 1 start\n");

    pthread_cleanup_push(cleanup, "thread 1 first handler");

    pthread_cleanup_push(cleanup, "thread 1 second handler");

    printf("thread 1 push complete\n");

    if (arg)

    {

        return ((void *)1);

    }

    pthread_cleanup_pop(0);

    pthread_cleanup_pop(0);

    return ((void *)1);

}


void * thr_fn2(void *arg)

{

    printf("thread 2 start \n");

    pthread_cleanup_push(cleanup, "thread 2 first handler");

    pthread_cleanup_push(cleanup, "thread 2 second handler");

    printf("thread 2 push complete\n");

    if (arg)

    {

        pthread_exit((void *)2);

    }

    pthread_cleanup_pop(0);

    pthread_cleanup_pop(0);

    pthread_exit((void *)2);

}


int main(void)

{

    int err;

    pthread_t tid1, tid2;

    void *tret;


    err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);


    if (err != 0 )

        err_quit("can't create thread 1: %s\n", strerror(err));


    err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);


    if (err != 0 )

        err_quit("cna't create thread 2: %s\n", strerror(err));


    err = pthread_join(tid1, &tret);

    if (err != 0)

        err_quit("can't join with thread 1: %s \n", strerror(err));

    printf("thread 1 exit code %d \n", (int)tret);

    err = pthread_join(tid2, &tret);


    if(err != 0)

        err_quit("can't join with thread 2: %s \n", strerror(err));

    printf("thread 2 exit code %d\n", (int)tret);

    exit(0);


}


$ ./11-5

thread 2 start

thread 1 start

thread 1 push complete

thread 2 push complete

thread 1 exit code 1

cleanup : thread 2 second handler

cleanup : thread 2 first handler

thread 2 exit code 2


return으로 종료된 경우는 스레드 마무리 처리부를 처리하지 않았다.

pthread_exit 로 종료한 경우에 스레드 마무리 처리부를 호출하였다.


출처 : 유닉스고급프로그램 11-5


Posted by Triany