线程的终止

线程终止

进程的终止:

1.进程中的任意线程调用了exit、_Exit 或者 _exit,那么整个进程就会终止。
2.如果默认的动作是终止进程,那么发送到线程的信号也会终止整个进程。

线程的终止:

在不终止进程的情况下停止单个线程:
1.线程可以从启动例程中返回(return),返回值是线程的退出码。
2.线程可以被同一进程中的其他线程取消。
3.线程调用pthread_exit。



#include < pthread. h>
void pthread_exit( void *rval_ptr);
//结束线程

rval_ptr 参数是一个无类型指针,保存线程的退出码。



#include < pthread. h>
int pthread_join( pthread_t thread, void **rval_ptr);
//返回值:若成功,返回0;否则,返回错误编码。

调用该函数的线程将一直阻塞,等待直到指定的线程终止。
如果线程简单地从它的启动例程返回,rval_ptr就包含返回码。如果线程被取消,由rval_ptr指定的内存单元就被设置为PTHREAD_CANCELED,如果不需要知道线程的返回值,则可以设置为NULL。

可以通过调用该函数来使线程置于分离状态,这样在线程结束后,所使用的资源将得到恢复。
如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。



#include 
#include 
#include 
#include 

void *thr_fn1(void *arg){
    printf("thread 1 returning\n");
    return ((void *)1);
}

void *thr_fn2(void *arg){
    printf("thread 2 exiting\n");
    pthread_exit((void *)2);
}

int main(int argc, const char * argv[]) {

    int err;
    pthread_t tid1,tid2;
    void *tret;
    err = pthread_create(&tid1, NULL, thr_fn1, NULL);//创建子线程1
    if (err != 0) {
        printf("can't create thread 1");
        exit(-1);
    }
    err = pthread_create(&tid2, NULL, thr_fn2, NULL);//创建子线程1
    if (err != 0) {
        printf("can't create thread 2");
        exit(-1);
    }
    err = pthread_join(tid1, &tret); //阻塞等待线程1结束
    if (err != 0) {
        printf("can't join with thread 1");
        exit(-1);
    }
    printf("thread 1 exit code %ld\n",(long)tret);
    err = pthread_join(tid2, &tret);//阻塞等待线程2结束
    if (err != 0) {
        printf("can't join with thread 2");
        exit(-1);
    }
    printf("thread 2 exit code %ld\n",(long)tret);
    exit(0);
}

运行结果:

thread 1 returning
thread 2 exiting
thread 1 exit code 1
thread 2 exit code 2
Program ended with exit code: 0

线程取消


#include < pthread. h>
int pthread_cancel( pthread_t tid);

//返回值:若成功,返回0;否则,返回错误码。

线程可以通过调用pthread_cancel来取消同一进程中的其他线程。
pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用参数PTHREAD_CANCELED的pthread_exit函数,但是,线程可以选择忽略取消或者控制如何被取消。

注意:

pthread_cancel不等待线程终止,它仅仅提出要求。


线程清理处理函数

线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。
一个线程可以安排多个线程清理处理程序,这些处理程序记录在栈中,也就是说,它们的执行顺序与它们注册时相反。


#include < pthread. h>
void pthread_cleanup_push( void (*rtn)( void *), void *arg);
void pthread_cleanup_pop( int execute);

当线程执行以下动作时,清理函数rtn是由pthread_ cleanup_ push函数调度的,调用时,只有一个参数arg:

1.调用pthread_exit时;
2.响应取消请求时;
3.用非零execute参数调用pthread_ cleanup_ pop函数时;
如果execute参数为0,清理函数将不被调用。

注意:

不管发生上述哪种情况,pthread_cleanup_pop都将删除上一次pthread_cleanup_push调用建立的清理函数,pthread_cleanup_pop一次只清理一个,所以一般在使用时要pthread_cleanup_push和pthread_cleanup_pop成对出现,否则有可能编译不通过。

实例


#include 
#include 
#include 
#include 

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_exit((void *)1);//触发清理函数
    }

    //一般pop和push是成对出现的,因为有些平台是用宏定义的函数,可能定义时push后面多带了个‘{’,而pop后面多带了一个‘}’,所以用0作为参数的pop和push进行配对
    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) {
        //return ((void *)2);
        pthread_exit((void *)2);//触发清理函数
    }

    //一般pop和push是成对出现的,因为有些平台是用宏定义的函数,可能定义时push后面多带了个‘{’,而pop后面多带了一个‘}’,所以用0作为参数的pop和push进行配对
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return ((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) {
        printf("can't create thread 1");
    exit(-1);
    }
    err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
    if (err != 0) {
        printf("can't create thread 2");
        exit(-1);
    }
    err = pthread_join(tid1, &tret);
    if (err != 0) {
        printf("can't join with thread 1");
        exit(-1);
    }
    printf("thread 1 exit code %ld\n",(long)tret);
    err = pthread_join(tid2, &tret);
    if (err != 0) {
        printf("can't join with thread 2");
        exit(-1);
    }
    printf("thread 2 exit code %ld\n",(long)tret);
    exit(0);
}

执行结果:

thread 1 start
thread 1 push complete
cleanup: thread 1 second handler
cleanup: thread 1 first handler
thread 1 exit code 1
thread 2 start
thread 2 push complete
cleanup: thread 2 second handler
cleanup: thread 2 first handler
thread 2 exit code 2
Program ended with exit code: 0

注意:

后push的清理函数先执行,栈式记录。
pthread_cleanup_push和pthread_cleanup_pop之间不能调用retrn,否则有可能出现段错误而使程序崩溃。
因为某些平台下这两个函数是用宏定义实现的,宏把某些上下文存放到栈上,而清理函数也是被放到栈上。当线程1在调用pthread_cleanup_push和调用pthread_cleanup_pop之间返回时,栈已被改写(return后相应的函数就得出栈,栈被改变),而再次调用清理函数时就使用了这个被改变的栈,上下文内容被破坏,就出现段错误崩溃,所以使用清理函数的线程结束时应该调用pthread_exit而不应该使用return。


线程分离

默认情况下,线程的终止状态会保存直到对该线程调用pthread_join。如果线程已经被分离,线程的底层可以在线程终止时立即被收回。
在线程分离后,我们不能使用pthread_join等待线程的终止状态,因为对分离的线程调用pthread_join会产生未定义的行为。

可以调用pthread_ detach分离线程。

#include < pthread. h>
int pthread_ detach( pthread_ t tid);
//返回值:若成功,返回0;否则返回失败编码。

你可能感兴趣的:(Linux程序设计,线程,线程终止,Linux)