Linux 多线程pthread_join(tid)返回值为22原因.

首先直接说明为什么返回值是22。
若线程的状态是,unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
如果线程是joinable状态,只有当你调用了pthread_join之后这些资源才会被释放,也就是说只有不设置unjoinable状态时pthread_join才有效,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符。因此需要搞清楚线程的状态属性。
那么什么是线程的分离状态呢?
线程和进程一样,在内核中拥有进程控制结构存储线程结束状态之类的信息,所以,系统中同样会出现僵尸线程。不过当进程结束运行时,这种僵尸线程就会随着进程的消失而消失,因此影响不是很严重。如果想避免这种僵尸线程,则应该将线程设置为结束状态分离,这是线程的结束状态将不能再被线程中的其他线程得到,同时,保存线程结束状态信息的存储区域也将变得不能引用!这就是被设置成分离状态后再使用pthread_join()返回值为22的原因。
在创建线程的时候,可以指定线程的结束状态和线程分离。在pthread_create()一个线程时,将属性设置为PTHREAD_CREATE_DETACHED,表示该线程时一个状态分离的线程,最后调用pthread_join()函数验证该过程,确实已经分离。
另外还有一种方法就是在线程体函数调用pthread_detach(pthread_t tid),是线程设置为unjoinable(分离)状态。若在线程体函数入口处调用pthread_detach(pthread_self()),则可以将线程体自身变为分离状态。
接下来再介绍个检测某个线程是否还存活的方法。

/*pthread_kill的返回值:成功(0) /线程不存在(ESRCH)/ 信号不合法(EINVAL)*/
void pth_isalive(pthread_t tid) 
{
    int pthread_status;
    pthread_status = pthread_kill(tid,0);

    if(pthread_status == ESRCH)
        printf("ID为0x%x的线程不存在或者已经退出。\n",(unsigned int)tid);
    else if(pthread_status == EINVAL)
        printf("发送信号非法。/n");
    else
        printf("ID为0x%x的线程目前仍然存活。\n",(unsigned int)tid);
}
/*线程体函数*/
void *rru_recv_thread(void *arg)//req,rep接收线程.
{
    printf("thread begin run!\n"); 
    //pthread_detach(pthread_self());将线程设置为分离态。
    ARG *p  = (ARG *)arg;
    char *buf=NULL;
    int bytes = -1;

    bytes = nn_recv(p->arg1,&buf,NN_MSG,0);
    assert(bytes>=0);
    pth_res = 1;
    struct command_rep *rep = (struct command_rep *)buf;
    printf("set LD_MODE to :%s return %d\n", rep->ack.errstr, rep->ack.code);
    nn_freemsg (buf);

    pthread_exit(NULL);
}
void rru_set_ld_mode(int sock,int value)
{
  int bytes = -1;
  void *res;
  float runtime=0;
  pth_res = -1;
  pthread_t rru_recv_pth;
  arg.arg1 = sock;
  //printf("debug--file:%s,-line %d\n",__FILE__, __LINE__);
  struct timeval tpstart,tpend;
  struct command_req req;
  struct cmd_set_int *set = (struct cmd_set_int *)&(req.req);
  req.hdr.type = CMD_TYPE_RRU;
  req.hdr.id = SET_LD_MODE;

  strncpy(set->name,"LD_MODE",sizeof(set->name));
  set->value[0] = value;
  gettimeofday(&tpstart,NULL);
  int ret = pthread_create(&rru_recv_pth,NULL,rru_recv_thread,(void *)&arg);
  printf("thread 0x%x has been establish!\n",(unsigned int)rru_recv_pth);
  bytes = nn_send(sock,&req,CMD_REQ_LEN,0);
  assert(bytes == CMD_REQ_LEN);
  while(1){
        gettimeofday(&tpend,NULL);
        runtime=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
        if(pth_res>0)
                return ;
        if(runtime>=3000000.0){//子线程是否运行有3S,
                ret = pthread_cancel(rru_recv_pth);
                printf("ret is %d\n",ret);
                if(ret!=0){
                        printf("can`t cancel thread %s\n",strerror(ret));
                        exit(1);
                }
                //pth_isalive(rru_recv_pth); //位置1
                printf("pthread timeout\n"); 
                runtime = 0;
                pth_res = -1;
                ret = pthread_join(rru_recv_pth,&res);
                printf("join ret is %d\n",ret);
                if(ret!=0){
                        printf("can`t join thread %s\n",strerror(ret));
                        exit(1);
                }
                if(res == PTHREAD_CANCELED)
                        printf("thread %u has been canceled\n",(unsigned int)rru_recv_pth);
                else
                        printf("thread cancel error\n");   
                pth_isalive(rru_recv_pth);  //位置2
                return ;                  
        }
   }
}

这是用到的一段代码,当线程体函数中带有pthread_detach(pthread_self())时,即处于分离态,在位置1处检测线程运行状态。结果是:
这里写图片描述
可以看到即使pthread_cancel成功返回,线程依然存活。这是因为并没有释放掉线程的资源,线程仍然存活着。而且使用pthread_join()返回值是22,函数出错,并没有成功回收资源。
如果把线程体中pthread_detach(pthread_self())注释掉,再分别在位置1和位置2处都加上检测线程是否存活。结果如下:
这里写图片描述
可以看出,未调用pthread_join,线程还存活,成功调用后可以看到线程已经退出。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值