linux线程间的通信(pthread_cleanup_push和pthread_cleanup_pop,pthread_join,pthread_detach)

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

pthread_cleanup_push和pthread_cleanup_pop函数原型如下:

      头文件:#include <pthread.h>

      函数原型:void pthread_cleanup_push(void (*rtn)(void *), void *arg);

                         void pthread_clean_pop(int execute);

      void(*rtn)(void *): 线程清理函数

      另外简单记录下pthread_cancel函数。该函数为线程取消函数,用来取消同一进程中的其他进程,函数原型:

      头文件: #include <pthread.h>

      函数原型:pthread_cancel(pthread_t tid);

      tid: 线程id

     当线程执行以下动作时,调用清理函数,调用的参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。

     ●调用pthread_exit时

     ●响应取消请求时

     ●用非零execute参数调用pthread_cleanup_pop时 (??)    

当pthread_cleanup_pop()函数的参数为0时,仅仅在线程调用pthread_exit函数或者其它线程对本线程调用 pthread_cancel函数时,才在弹出“清理函数”的同时执行该“清理函数”。

同样我也取了博客中的第二个例子来说明pthread_cancel调用时,pthread_cleanup_push会用清理函数。

(ps:“不管上述哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理函数”,所以可能作者想表达的是设置为pop参数设置为0,在其它两种情况下同样会被调用)

#include <stdio.h>
#include <stdlib.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()
{
    int err;
    pthread_t tid1,tid2;
    void *tret;
    err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
    if(err != 0)
    {
        fprintf(stderr,"thread create 1 is error\n");
        return -1;
    }
    err = pthread_create(&tid2,NULL,thr_fn2,(void *)1);
    if(err != 0)
    {
        fprintf(stderr,"thread create 2 is error\n");
        return -2;
    }
    err = pthread_join(tid1,&tret);
    if(err != 0)
    {
        fprintf(stderr,"can't join with thread 1\n");
        return -2;
    }
 
    //pthread_cancel(tid1);
    printf("thread 1 exit code %d\n",tret);
    err = pthread_join(tid2,&tret);
    if(err != 0)
    {
        fprintf(stderr,"can't join with thread 2\n");
        return -2;
    }
    printf("thread 2 exit code %d\n",tret);
    return 0;
}

运行结果如下:

从输出结果可以看出:两个线程都调用了,但是却只调用了第二个线程的清理处理程序,所以如果线程是通过从它的启动历程中返回而终止的话,那么它的清理处理程序就不会被调用,

还要注意清理程序是按照与它们安装时相反的顺序被调用的。从代码输出也可以看到先执行的thread 2 second handler后执行的thread 2 first handler。

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void clean_fun1(void * arg)
{
    printf("this is clean fun1\n");
}
void clean_fun2(void * arg)
{
    printf("this is clean fun2\n");
}
void * thread_fun(void * arg)
{
    pthread_cleanup_push(clean_fun1,NULL);
    pthread_cleanup_push(clean_fun2,NULL);
    sleep(100);
    //这里要注意,如果将sleep(100);换成while(1);的话,程序会一直暂停.push和pop要成对出现.
    //因为while(1);运行的太快,线程不接受cancel信号
    //while(1);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return NULL;
}
int main()
{
    pthread_t tid1;
    int err;
    err=pthread_create(&tid1,NULL,thread_fun,NULL);
    if(err!=0)
    {
        perror("pthread_create");
        exit(0);
    }
    sleep(3);
    //printf("test\n");
    err=pthread_cancel(tid1);
    if(err!=0)
    {
        perror("cancel error:");
        exit(0);
    }
    err=pthread_join(tid1,NULL);
    if(err!=0)
    {
        perror("pthread_join error:");
        exit(0);
    }
 
    return 0;
}

运行结果如下:

从上面也可以看出,当调用pthread_cancel函数请求后,等到响应请求时,代码调用了pthread_clean_push函数中的clean_fun1和clean_fun2,函数clean_fun2中的语句先被打印。

pthread_join()与pthread_detach()详解

1.linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,

如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。

若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。

2.unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.

3.其实简单的说就是在线程函数头加上 pthread_detach(pthread_self())的话,线程状态改变,在函数尾部直接 pthread_exit线程就会自动退出。省去了给线程擦屁股的麻烦。

/pthread_detach(pthread_self());
//使线程分离出来。当这个线程执行完成任务后释放释放资源。不然它会保留退出状态,等待别人来取。
pthread_detach(threadid)和pthread_detach(pthread_self())没有什么区别吧!有很严格的区别吗???如果非要讲区别不可,我觉得应该是调用他们的线程不同。
      pthread_detach(threadid)函数的功能是使线程ID为threadid的线程处于分离状态,一旦线程处于分离状态,该线程终止时底 层资源立即被回收;否则终止子线程的状态会一直保存(占用系统资源)直到主线程调用pthread_join(threadid,NULL)获取线程的退 出状态。通常是主线程使用pthread_create()创建子线程以后,一般可以调用pthread_detach(threadid)分离刚刚创建的子线程,这里的threadid是指子线程的threadid;如此以来,该子线程止时底层资源立即被回收;被创建的子线程也可以自己分离自己,子线程调用pthread_detach(pthread_self())就是分离自己,因为pthread_self()这个函数返回的就是自己本身的线程ID;

猜你喜欢

转载自blog.csdn.net/wteruiycbqqvwt/article/details/112791025