随笔日记: 一个daemon程序,它的pthread_join()经常意外返回, 很是奇怪!

本文探讨了一个TCP服务器监听线程未按预期退出的问题。通过分析代码流程,发现即使在AcceptFunc中未发生socket错误,主线程仍然过早终止了监听线程。进一步在AcceptFunc中引入异常处理机制后,问题依然存在。最终,作者通过深入分析,揭示了问题可能源于主线程未正确等待监听线程结束的原因。
 


TcpServer_Listener::Initialize() {
  ...
    pthread_create(&_ptThreadHandleAccept, NULL, AcceptThread, (void *)this);
}

void*
TcpServer_Listener::AcceptThread(void* pParam)
{
    pthread_detach(pthread_self());
    dbg_Printf(" TcpServer_Listener::AcceptThread Pid=%d.", getpid());    //print pthread_t is meaningless!

    TcpServer_Listener *pListener = reinterpret_cast<TcpServer_Listener *>(pParam);
    pListener->_bRunning = true;
    pListener->AcceptFunc();

    dbg_Printf(" TcpServer_Listener::AcceptThread Pid=%d exit.\n", getpid()); 
    return 0; // We never reach this point
}   

TcpServerListener::AcceptFunc(void)
{
    dbg_Printf("TcpServer_Listener enter listening loop");          // (1)
    while(true) {
        // 用 select 函数监听并接受连接
        // 如果有错误发生, 执行 break;
 }
    dbg_Printf("TcpServer_Listener enter listening loop");          // (2)
 return;


void TcpServer_Listener::WaitAcceptThreadEnding()
{
    if(_ptThreadHandleAccept != 0) {
        pthread_join(_ptThreadHandleAccept, NULL);
        dbg_Printf(" Accept(listening) thread finished!");                 // (3)
    }
}

int main(int argc, char* argv[]) {
       
  ... //do other jobs
  
 CdnDir_Listner  listener;
 if (!listener.Initialized(CDN_DIR_IP, CDN_DIR_PORT)) {
        dbg_Printf("Server exit because failed creating listener");
        return 1;
    }
    dbg_Printf("Server running...");                                                       // (4)
    listener.WaitAcceptThreadEnding();
 return 0;
}

   现在运行中遇到一个问题, 偶发的:
       AcceptThread 线程还没有退出( 从代码(2)处的 log没有打印出来看出来的 ),
   但是, 整个main程序会没等到 AcceptThread 结束就退出了,打印出了代码(3)处的log。
  
  
   下面的两组信息分别是正确时、 错误时的:
  
   正确时的:
/home/work # ./cdn_dir_daemon &
     。。。    。。。
[ cdn_dir_daemon_c2 info ] [FILE: cdn_dir.cpp, Func: main() ]Server running...
[ cdn_dir_daemon_c2 info ] [FILE: bxLite_tcp_utils.cpp, Func: AcceptThread() ] TcpServer_Listener::AcceptThread Pid=11143.
[ cdn_dir_daemon_c2 info ] [FILE: bxLite_tcp_utils.cpp, Func: AcceptFunc() ] TcpServer_Listener enter listening loop


    出错时的:
/home/work # ./cdn_dir_daemon &
   。。。    。。。
[ cdn_dir_daemon_c2 info ] [FILE: cdn_dir.cpp, Func: main() ]Server running...
[ cdn_dir_daemon_c2 info ] [FILE: bxLite_tcp_utils.cpp, Func: AcceptThread() ] TcpServer_Listener::AcceptThread Pid=11130.
[ cdn_dir_daemon_c2 info ] [FILE: bxLite_tcp_utils.cpp, Func: AcceptFunc() ] TcpServer_Listener enter listening loop
[ cdn_dir_daemon_c2 info ] [FILE: bxLite_tcp_utils.cpp, Func: WaitAcceptThreadEnding() ] Accept(listening) thread finished!


       怎么会这么就退出了呢??? 
   
    如果AcceptFunc中没有发生socket错误,那么AcceptThread是不会退出的。
   
    可是我在AcceptFunc中会发生socket错误的地方都有log打印。而看到的出错时的log并没有看到。
 
        我后来在AcceptFunc中用 try{}catch(...) { dbg_Print("A unknown exception happen")} , 也是没看到AcceptThread线程退出而pthread_join()就返回了!!!
  
  很奇怪啊...
  
  

<think>我们正在讨论pthread_join函数的作用。用户询问的是pthread_join(threads[i], NULL);的作用。 根据之前关于pthread的讨论,我们知道pthread_join用于等待指定线程结束。 因此,我们需要详细解释这个函数调用的作用,特别是当第二个参数为NULL时的行为。 回答结构: 1. 解释pthread_join函数的作用。 2. 说明参数含义:第一个参数是线程ID,第二个参数用于接收线程的返回值。 3. 当第二个参数为NULL时,表示不关心线程的返回值。 4. 强调该函数会阻塞调用线程,直到目标线程结束。 5. 如果不调用pthread_join,可能会导致资源泄漏(如线程资源未释放)或僵尸线程。 6. 对比pthread_detach,说明join的必要性。 然后,按照要求提出3-5个相关问题。</think>`pthread_join(threads[i], NULL);` 是 POSIX 线程编程中的关键函数,用于线程同步和资源管理。以下是详细解析: ### 核心作用与原理 ```c int pthread_join(pthread_t thread, void **retval); ``` - **阻塞调用线程**:当前线程(通常是主线程)会暂停执行,直到目标线程 `thread` 终止 - **资源回收**:自动释放目标线程占用的系统资源(线程栈、寄存器状态等) - **返回值获取**:`retval` 参数接收目标线程的返回值(当为 `NULL` 时表示不关心返回值) - **线程同步**:确保目标线程完成工作后再继续执行后续代码 ### 工作流程详解 ```mermaid graph TD A[主线程] -->|创建| B[线程1] A -->|创建| C[线程2] A -->|创建| D[线程3] A -->|pthread_join 线程1| B A -->|pthread_join 线程2| C A -->|pthread_join 线程3| D B -->|终止| E[释放资源] C -->|终止| F[释放资源] D -->|终止| G[释放资源] E --> H[主线程继续] F --> H G --> H ``` ### 使用场景示例 #### 1. 基本等待 ```c pthread_t thread; void* result; // 创建线程 pthread_create(&thread, NULL, worker_func, NULL); // 等待线程结束(不获取返回值) pthread_join(thread, NULL); // 关键点:阻塞主线程直到worker_func完成 ``` #### 2. 获取线程返回值 ```c void* worker_func(void* arg) { int* result = malloc(sizeof(int)); *result = 42; return result; // 返回动态分配的内存 } int main() { pthread_t thread; void* ret_val; pthread_create(&thread, NULL, worker_func, NULL); // 等待线程结束并获取返回pthread_join(thread, &ret_val); // 关键点:获取返回值指针 int* int_result = (int*)ret_val; printf("Result: %d\n", *int_result); // 输出: Result: 42 free(ret_val); // 释放线程分配的内存 return 0; } ``` #### 3. 多线程同步 ```c #define NUM_THREADS 5 void* task(void* id) { printf("Thread %d working\n", *(int*)id); sleep(1); return NULL; } int main() { pthread_t threads[NUM_THREADS]; int ids[NUM_THREADS]; // 创建所有线程 for (int i = 0; i < NUM_THREADS; i++) { ids[i] = i; pthread_create(&threads[i], NULL, task, &ids[i]); } // 等待所有线程完成 for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); // 关键点:确保所有线程完成 } printf("All tasks completed\n"); return 0; } ``` ### 重要注意事项 1. **必须调用情形**: - 线程需要返回值给创建者 - 主线程依赖子线程的处理结果 - 需要确保资源正确释放 ```c // 错误示例:未join导致内存泄漏 pthread_create(&thread, NULL, mallocing_func, NULL); // 忘记pthread_join → malloc分配的内存永远不会释放 ``` 2. **避免调用情形**: - 使用分离线程时 (`pthread_detach`) - 线程是守护线程(长期运行不需要同步) ```c pthread_create(&daemon_thread, NULL, daemon_func, NULL); pthread_detach(daemon_thread); // 不需要join ``` 3. **返回值处理**: ```c void* thread_func(void* arg) { // 返回栈上变量会导致未定义行为! // int result = 42; return &result; // 错误! // 正确做法:返回堆分配内存 int* result = malloc(sizeof(int)); *result = 42; return result; } ``` 4. **错误处理**: ```c int status = pthread_join(thread, &retval); if (status != 0) { // 常见错误: // ESRCH: 线程不存在 // EINVAL: 线程是分离线程 // EDEADLK: 死锁(尝试join自己) perror("pthread_join failed"); } ``` ### 与 `pthread_detach` 对比 | 特性 | `pthread_join` | `pthread_detach` | |---------------------|------------------------------------|-----------------------------------| | 资源回收 | 调用者负责回收 | 系统自动回收 | | 阻塞行为 | 阻塞调用线程 | 非阻塞 | | 返回值获取 | 支持 | 不支持 | | 多次调用 | 导致未定义行为 | 安全 | | 典型应用场景 | 需要同步/获取结果 | 后台任务/不需要交互的任务 | | 错误处理 | 必须检查返回值 | 通常忽略错误 | ### 高级应用:超时等待 ```c #include <time.h> #include <errno.h> int join_with_timeout(pthread_t thread, void** retval, int timeout_sec) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_sec; // 设置超时时间 return pthread_timedjoin_np(thread, retval, &ts); // Linux特有扩展 } // 使用示例 int main() { pthread_t thread; pthread_create(&thread, NULL, long_task, NULL); if (join_with_timeout(thread, NULL, 5) == ETIMEDOUT) { printf("Thread timed out, terminating...\n"); pthread_cancel(thread); // 强制终止线程 } return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值