pthread_create导致内存泄露

 这几天一直在调试一个系统,系统的功能就是定时发送数据、接收数据然后解析收到的数据,转换成一定的格式存入数据库中。我为了并发操作,所以每接收到一个数据包,就调用pthread_create函数创建一个默认属性的线程进行处理。

   系统一开始运行很正常,但是当接收到第299个数据包时,就发生异常,查看程序日志,得知原来自接收到299个数据包后,就不再解析接收到的数据。我本以为是网络的问题,于是,重启下程序,结果异常发生在了同样的位置。这时,我猜想可能是代码的问题,找到相关代码,如下:
 

while (1)
{
    len = recvfrom(sock, buf, MAXPACKETSIZE,

                    0,(struct sockaddr *)&c_addr, &addr_len);
    .......
    targ = (struct threadarg *)malloc(sizeof(struct threadarg));
    memset(targ, 0, sizeof(struct threadarg));
        
    targ->len = len;
    targ->ip = (int)c_addr.sin_addr.s_addr;
    memcpy(targ->buffer, buf, len);
    printf("received\n");
       

    //注:targ在线程中会被free掉。

    pthread_create(&tid, NULL, insertToList, (void *)targ);
}


从代码看不出什么异常,由于解析数据是调用pthread_create函数创建一个默认属性的线程进行处理,如果没有解析,那么,应该是pthread_create函数没有创建成功。而pthread_create函数创建失败最可能的原因应该就是系统资源不足,根据经验,线程的默认堆栈大小是1MB,就是说,系统每创建一个线程就要至少提供1MB的内存,那么,创建线程失败,极有可能就是内存不够用了。从代码中看不出有内存泄露的现象,有malloc的地方就会有free对应。而仍然出现问题,那么,唯一的解释就是pthread_create会导致内存泄露! pthread_create创建的线程结束后,系统并未回收其资源,从而导致了泄露。

    然后从网上查了相关资料如下:


    线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。


    从上面的描述中可以得知如果调用pthread_create函数创建一个默认非分离状态的线程,如果不用pthread_join()函数,线程结束时并不算终止,所以仍然会占用系统资源。这里有如下几种方法解决这个问题:
1.使用pthread_join()函数回收相关内存区域。
 

pthread_t tid;
void* state;

pthread_create(&tid, NULL, test, NULL);
pthread_join(tid, &state);


2.可以调用 pthread_detach() 函数分离线程。
 

pthread_t tid;
pthread_create(&tid, NULL, test, NULL);
pthread_detach(tid);


当然,也可以在 thread function 中调用。
 

void* test(void* arg)
{
    .....
    pthread_detach(pthread_self());
    return NULL;
}


3.使用线程属性。
 

pthread_attr_t attr;
pthread_t tid;

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_create(&tid, &attr, test, NULL);

sleep(3);//等待线程结束

pthread_attr_destroy(&attr);


根据实际需要,任选其一即可。

ps:最后,我写了个测试程序,然后用valgrind检查了一下。
测试程序:

#include <pthread.h>
#include <stdio.h>

void* test()
{
    printf("ok\n");
    return;
}
int main(int argc, char** argv)
{
        pthread_t tid;
        pthread_create(&tid, NULL, test, NULL);
    //pthread_join(tid, NULL);

    return 1;
}

编译链接:
[root@localhost ~]# gcc -g b.c -o b -lpthread
然后,用valgrind进行内存检查


[root@localhost ~]# valgrind --tool=memcheck --leak-check=full ./b
==20980== Memcheck, a memory error detector
==20980== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==20980== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==20980== Command: ./b
==20980==
ok
==20980==
==20980== HEAP SUMMARY:
==20980==     in use at exit: 272 bytes in 1 blocks
==20980==   total heap usage: 1 allocs, 0 frees, 272 bytes allocated
==20980==
==20980== 272 bytes in 1 blocks are possibly lost in loss record 1 of 1
==20980==    at 0x4C1F1A0: calloc (vg_replace_malloc.c:418)
==20980==    by 0x4010422: _dl_allocate_tls (in /lib64/ld-2.7.so)
==20980==    by 0x4E2AB52: pthread_create@@GLIBC_2.2.5 (in /lib64/libpthread-2.7.so)
==20980==    by 0x40059E: main (b.c:13)
==20980==
==20980== LEAK SUMMARY:
==20980==    definitely lost: 0 bytes in 0 blocks
==20980==    indirectly lost: 0 bytes in 0 blocks
==20980==      possibly lost: 272 bytes in 1 blocks
==20980==    still reachable: 0 bytes in 0 blocks
==20980==         suppressed: 0 bytes in 0 blocks
==20980==
==20980== For counts of detected and suppressed errors, rerun with: -v
==20980== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)


确实有内存泄露。
修改测试程序:
 

#include <pthread.h>
#include <stdio.h>

void* test()
{
    printf("ok\n");
    return;
}
int main(int argc, char** argv)
{
        pthread_t tid;
        pthread_create(&tid, NULL, test, NULL);
    pthread_join(tid, NULL);
    return 1;
}

编译链接:
[root@localhost ~]# gcc -g b.c -o b -lpthread
然后,用valgrind进行内存检查


[root@localhost ~]# valgrind --tool=memcheck --leak-check=full ./b
==21013== Memcheck, a memory error detector
==21013== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==21013== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==21013== Command: ./b
==21013==
ok
==21013==
==21013== HEAP SUMMARY:
==21013==     in use at exit: 0 bytes in 0 blocks
==21013==   total heap usage: 1 allocs, 1 frees, 272 bytes allocated
==21013==
==21013== All heap blocks were freed -- no leaks are possible
==21013==
==21013== For counts of detected and suppressed errors, rerun with: -v
==21013== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)


问题解决。

<think>嗯,用户让我解释一下pthread_create函数的声明,这个函数是用来创建线程的。首先,我需要回忆一下POSIX线程的相关知识。pthread_create是POSIX线程库中的一个重要函数,用于创建新的线程。它的参数看起来有点复杂,我得逐一分析每个参数的作用和类型。 首先看函数原型:extern int pthread_create (pthread_t *__restrict __newthread, const pthread_attr_t *__restrict __attr, void *(*__start_routine) (void *), void *__restrict __arg)。这里可能需要解释每个参数的含义,比如第一个参数是指向pthread_t的指针,用来保存新线程的ID。第二个参数是线程属性,通常可以设为NULL使用默认属性。第三个是线程函数的指针,这个函数接受void*参数并返回void*。第四个是传递给线程函数的参数。 接下来,用户可能想知道如何使用这个函数,比如具体的代码示例。可能需要提供一个简单的例子,说明如何定义线程函数,如何传递参数,以及如何编译链接,因为pthread库需要-lpthread选项。 另外,需要注意线程的错误处理,pthread_create返回0表示成功,非0表示错误,所以检查返回值很重要。还有线程的属性,如果用户不设置,用NULL就行,但如果有特殊需求,比如设置分离状态或栈大小,就需要使用pthread_attr_t相关函数。 用户可能对__restrict和__THROWNL这些修饰符不太熟悉,可能需要解释一下,比如__restrict是C99的关键字,表示指针是唯一访问该数据的途径,帮助编译器优化。__THROWNL可能表示函数不会抛出异常,但在C中异常处理不常见,可能更多是宏定义。 另外,线程安全方面,如果多个线程同时创建,需要注意传入的参数是否安全,比如arg参数如果多个线程共享,可能需要同步机制。还有线程函数的返回值,如何通过pthread_join获取。 可能还需要提醒用户,线程创建后,主线程需要决定是等待子线程结束(使用pthread_join)还是分离线程(使用pthread_detach),否则可能导致资源泄漏。 最后,编译时的链接选项需要加上-pthread或-lpthread,不同的编译器可能有不同的要求,需要指出常见的编译命令格式。 总结一下,回答需要分步骤,先介绍函数的作用,参数详解,返回值,使用示例,注意事项,常见错误和编译方法。确保结构清晰,帮助用户逐步理解如何使用这个函数。</think>以下是关于`pthread_create`函数的详细解析: --- ### **函数功能** `pthread_create`是POSIX线程库(pthread)中用于**创建线程**的核心函数。新线程会与调用线程并发执行。 --- ### **函数声明** ```c extern int pthread_create( pthread_t *__restrict __newthread, const pthread_attr_t *__restrict __attr, void *(*__start_routine)(void *), void *__restrict __arg ) __THROWNL __nonnull((1, 3)); ``` --- ### **参数详解** 1. **`pthread_t *__restrict __newthread`** - **作用**:输出参数,用于存储新线程的标识符(线程ID)。 - **注意**:必须指向有效的内存地址,线程ID由系统分配。 2. **`const pthread_attr_t *__restrict __attr`** - **作用**:设置线程属性(如栈大小、调度策略等)。 - **默认行为**:若为`NULL`,则使用默认属性。 - **高级用法**:可通过`pthread_attr_init`自定义属性。 3. **`void *(*__start_routine)(void *)`** - **作用**:新线程的入口函数指针。 - **函数签名**:必须接受一个`void *`参数并返回`void *`。 - **示例**: ```c void *thread_function(void *arg) { // 线程逻辑 return NULL; } ``` 4. **`void *__restrict __arg`** - **作用**:传递给`__start_routine`的参数。 - **技巧**:可通过结构体传递多个参数。 --- ### **返回值** - **成功**:返回`0`。 - **失败**:返回错误码(非零值),可通过`errno`或`strerror`解析。 --- ### **使用示例** ```c #include <pthread.h> #include <stdio.h> void *print_message(void *msg) { printf("Message: %s\n", (char *)msg); return NULL; } int main() { pthread_t tid; char *message = "Hello from thread!"; // 创建线程 int ret = pthread_create(&tid, NULL, print_message, message); if (ret != 0) { perror("Thread creation failed"); return 1; } // 等待线程结束 pthread_join(tid, NULL); return 0; } ``` --- ### **注意事项** 1. **线程生命周期** - 新线程与调用线程独立运行,主线程退出可能导致线程终止。 - 使用`pthread_join`等待线程结束,或`pthread_detach`分离线程。 2. **参数传递安全** - 确保`__arg`指向的数据在线程运行期间有效(避免传递栈变量后立即释放)。 3. **资源管理** - 线程结束后需释放其资源(如动态分配的内存)。 4. **编译选项** - 编译时需链接`pthread`库: ```bash gcc program.c -o program -lpthread ``` --- ### **错误处理** 常见错误码: - `EAGAIN`:资源不足(如线程数超出限制)。 - `EINVAL`:无效的线程属性。 - `EPERM`:权限不足。 --- 通过以上步骤,您可以安全地使用`pthread_create`实现多线程编程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值