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,线程还存活,成功调用后可以看到线程已经退出。

<think>好的,我现在要回答用户关于pthread_joinpthread_exit的使用关系的问题。首先,我需要确认这两个函数的基本功能以及它们在多线程编程中的角色。 根据提供的引用资料,pthread_exit用于终止当前线程,并可以传递一个返回值。而pthread_join则是用来等待指定线程结束,并获取其返回值。用户想知道这两个函数是否必须一起使用,以及如何正确配合使用。 首先,我得回忆一下线程的基本生命周期。当一个线程创建后,它开始执行指定的函数。当这个函数执行完毕返回时,线程会自动终止。在这种情况下,是否需要显式调用pthread_exit呢?比如,如果线程函数正常返回,可能不需要调用pthread_exit,因为返回语句已经隐含了线程的退出。但如果有中途退出的需求,或者需要传递返回值,这时候就需要显式调用pthread_exit了。 接下来,关于pthread_join的作用。它用于阻塞当前线程,直到目标线程结束,并获取其退出状态。如果不调用pthread_join,可能会导致资源泄漏,因为线程的资源可能没有被正确回收。但有时候可能使用detach来让线程自动释放资源,这样就不需要join了。不过用户的问题集中在join和exit的关系上,所以需要明确这两个函数之间的关联。 用户的问题核心是这两个函数是否必须一起使用。根据引用[3],pthread_join用于等待线程退出,并获取其返回值,而这个返回值可能由pthread_exit设置。因此,当线程使用pthread_exit退出时,主线程或其他线程可以通过pthread_join来获取这个返回值。但并不是所有情况下都必须一起使用。例如,如果线程没有调用pthread_exit,而是正常返回,那么它的返回值就是线程函数的返回值,此时同样可以通过pthread_join来获取。 那是否需要每次创建线程都必须调用pthread_join呢?不一定,如果线程被设置为detached状态,或者主线程不关心线程的结束状态,可以不调用。但如果没有detach,又不调用join,就会导致资源无法回收,产生僵尸线程。因此,正确的做法通常是:要么join,要么detach线程。 现在回到用户的问题,是否必须一起使用。答案应该是否定的。线程可以通过返回或pthread_exit终止,而pthread_join的作用是等待线程结束并回收资源,无论线程是如何终止的。因此,pthread_exit和pthread_join之间没有必然的必须一起使用的关系,但它们在需要获取线程退出状态时配合使用。 接下来需要总结正确的使用方式。比如,主线程调用pthread_create创建线程后,可以选择调用pthread_join等待其结束。而在线程函数中,可以选择return或者调用pthread_exit来结束线程,并传递返回值。此时,pthread_join的第二个参数可以获取这个返回值。 同时要注意,主线程如果调用pthread_exit,只会退出主线程,而不会终止整个进程,其他线程继续运行。而如果主线程用exit,整个进程会终止。这一点在引用[2]中提到过,需要避免在线程中使用exit,而应该用pthread_exit。 最后,应该给出示例代码,说明如何配合使用这两个函数。比如,线程函数中使用pthread_exit返回一个值,主线程中使用pthread_join来接收。或者线程函数正常返回,主线程同样可以用pthread_join获取返回值。两种情况都需要正确使用。 还要注意参数传递的正确性,比如pthread_exit的参数是一个void指针,而pthread_join的第二个参数是void**类型,用于接收这个指针。需要确保传递的指针在线程结束后仍然有效,比如不要传递局部变量的地址,否则可能导致悬挂指针。 综上所述,回答的结构应该是先解释两个函数的作用,再说明它们之间的关系,是否需要一起使用,然后给出正确使用的方法和示例,最后总结注意事项。同时,引用提供的参考资料来支持结论。</think>### pthread_join()pthread_exit()的配合使用解析 #### 1. 函数功能对比 - **pthread_exit()**:终止当前线程并返回一个值,该值可被其他线程通过`pthread_join()`获取。例如: ```c pthread_exit((void*)42); // 返回整型值42 ``` 若线程函数正常返回(如`return (void*)0`),其效果等同于调用`pthread_exit()`[^2]。 - **pthread_join()**:阻塞调用线程,直到目标线程结束,并接收其返回值。例如: ```c void *retval; pthread_join(thread_id, &retval); // 获取目标线程的返回值 ``` #### 2. 是否需要配合使用? - **否**:两者没有强制绑定关系,但需根据需求选择: - **需要获取返回值时**:必须通过`pthread_join()`接收`pthread_exit()`或线程函数返回的值。 - **无需返回值时**:可选择分离线程(`pthread_detach()`),此时无需调用`pthread_join()`[^3]。 #### 3. 正确配合方法 **场景1**:主线程等待子线程返回结果 ```c // 子线程函数 void* thread_func(void* arg) { // ... 业务逻辑 pthread_exit((void*)"done"); // 显式返回结果 } int main() { pthread_t tid; void* retval; pthread_create(&tid, NULL, thread_func, NULL); pthread_join(tid, &retval); // 阻塞等待并获取返回值 printf("子线程返回: %s\n", (char*)retval); return 0; } ``` **场景2**:子线程自动回收资源 ```c // 子线程函数 void* thread_func(void* arg) { pthread_detach(pthread_self()); // 分离线程 // ... 业务逻辑 return NULL; // 无需显式调用pthread_exit } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_func, NULL); // 无需调用pthread_join return 0; } ``` #### 4. 关键注意事项 - **返回值生命周期**:通过`pthread_exit()`返回的指针必须指向全局或堆内存,避免返回局部变量地址(会导致悬垂指针)[^1]。 - **主线程行为**:若主线程调用`pthread_exit()`,仅主线程退出,其他线程继续运行;若调用`exit()`,整个进程终止。 - **资源泄漏**:未分离且未`join`的线程会占用系统资源,最终导致内存泄漏。 #### 5. 典型错误示例 ```c void* thread_func(void* arg) { int local_var = 100; pthread_exit((void*)&local_var); // 错误!局部变量地址在函数返回后失效 } ``` 此时通过`pthread_join()`获取的指针将指向无效内存。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值