gpt4 book ai didi

c pthreads + valgrind = 内存泄漏 : why?

转载 作者:太空狗 更新时间:2023-10-29 17:23:48 25 4
gpt4 key购买 nike

我正在开始使用 C 中的 pthreads,我也是一个疯狂的人,我尽可能地把我的代码写成“无错误”。

尽管试图格外小心,但 valgrind 告诉我,无论天气如何,我都在泄漏内存:

  1. 我创建了我在完成后加入的可连接线程(代码片段 1)
  2. 我创建了可连接的线程,我在创建后将其分离(代码片段 2)
  3. 我创建分离线程(代码片段 3)

我知道这已经被讨论过(参见 thisthisthis ),但我仍然很好奇:

  1. 为什么在某些运行中我最终没有出错?
  2. 为什么在处理分离线程时似乎有一个随机数的整体 mallocs()? << nos 提供的答案,代码片段“已修复”,并在 main() 中增加了延迟
  3. 为什么即使在处理分离线程时“内存泄漏”仍然存在? << 与 2 相同。

正如我从之前的答案和 valgrind 跟踪中了解到的那样,pthread_create() 是根本原因,根据需要扩展线程使用的堆栈并有时重用它,因此缺少一些释放。但不太清楚的是为什么它取决于执行运行以及为什么它也会在创建分离线程时发生。正如我从某些答案、评论以及该人那里看到的那样,线程完成后将释放分离线程的资源。我已经尝试了各种调整来解决这个问题(在每个线程结束之前添加一个 sleep 时间,在主线程结束之前,增加堆栈大小,添加更多“工作”......)但它并没有改变最终结果差了很多。另外,为什么在处理分离线程时整体“mallocs()”的数量是随机的,valgrind 是否会丢失一些分离线程?这似乎也不取决于堆栈大小。

所提供的代码是一个经理/ worker 模型的模拟示例,恕我直言,线程管理的 joinable/join() 方法似乎更适合。

感谢您提供的任何启发!我也希望这些(过度评论的)代码片段对任何希望开始使用 pthreads 的人有所帮助。

- 交换

PS 系统信息:debian 64 位 arch 上的 gcc

代码片段 1(已加入可加入的线程):

/* Running this multiple times with valgrind, I sometimes end with :
- no errors (proper malloc/free balance)
- 4 extra malloc vs free (most frequently)
The number of mallocs() is more conservative and depends on the number of threads.
*/

#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */
#include <stdio.h> /* printf() & the likes */
#include <pthread.h> /* test subject */

#define MAX_THREADS 100 /* Number of threads */
pthread_attr_t tattr; /* Thread attribute */
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */

/* A mock container structure to pass arguments around */
struct args_for_job_t {
int tid;
int status;
};

/* The job each worker will perform upon creation */
void *job(void *arg)
{
/* Cast arguments in a proper container */
struct args_for_job_t *container;
container = (struct args_for_job_t *)arg;

/* A mock job */
printf("[TID - %d]\n", container->tid);

/* Properly exit with status code tid */
pthread_exit((void *)(&container->status));
}

int main ()
{
int return_code; /* Will hold return codes */
void *return_status; /* Will hold return status */
int tid; /* Thread id */
struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */

/* Initialize and set thread joinable attribute */
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);

/* Spawn detached threads */
for (tid = 0; tid < MAX_THREADS; tid++)
{
args[tid].tid = tid;
args[tid].status = tid;
return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid]));
if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; }
}

/* Free thread attribute */
pthread_attr_destroy(&tattr);

/* Properly join() all workers before completion */
for(tid = 0; tid < MAX_THREADS; tid++)
{
return_code = pthread_join(workers[tid], &return_status);
if (return_code != 0)
{
printf("[ERROR] Return code from pthread_join() is %d\n", return_code);
return EXIT_FAILURE;
}
printf("Thread %d joined with return status %d\n", tid, *(int *)return_status);
}

return EXIT_SUCCESS;
}

代码片段 2(创建后分离的线程):

/* Running this multiple times with valgrind, I sometimes end with :
- no errors (proper malloc/free balance)
- 1 extra malloc vs free (most frequently)
Most surprisingly, it seems there is a random amount of overall mallocs
*/

#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */
#include <stdio.h> /* printf() & the likes */
#include <pthread.h> /* test subject */
#include <unistd.h>

#define MAX_THREADS 100 /* Number of threads */
pthread_attr_t tattr; /* Thread attribute */
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */

/* A mock container structure to pass arguments around */
struct args_for_job_t {
int tid;
};

/* The job each worker will perform upon creation */
void *job(void *arg)
{
/* Cast arguments in a proper container */
struct args_for_job_t *container;
container = (struct args_for_job_t *)arg;

/* A mock job */
printf("[TID - %d]\n", container->tid);

/* For the sake of returning something, not necessary */
return NULL;
}

int main ()
{
int return_code; /* Will hold return codes */
int tid; /* Thread id */
struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */

/* Initialize and set thread joinable attribute */
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);

/* Spawn detached threads */
for (tid = 0; tid < MAX_THREADS; tid++)
{
args[tid].tid = tid;
return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid]));
if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; }
/* Detach worker after creation */
pthread_detach(workers[tid]);
}

/* Free thread attribute */
pthread_attr_destroy(&tattr);

/* Delay main() completion until all detached threads finish their jobs. */
usleep(100000);
return EXIT_SUCCESS;
}

代码片段 3(创建时分离的线程):

/* Running this multiple times with valgrind, I sometimes end with :
- no errors (proper malloc/free balance)
- 1 extra malloc vs free (most frequently)
Most surprisingly, it seems there is a random amount of overall mallocs
*/

#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */
#include <stdio.h> /* printf() & the likes */
#include <pthread.h> /* test subject */

#define MAX_THREADS 100 /* Number of threads */
pthread_attr_t tattr; /* Thread attribute */
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */

/* A mock container structure to pass arguments around */
struct args_for_job_t {
int tid;
};

/* The job each worker will perform upon creation */
void *job(void *arg)
{
/* Cast arguments in a proper container */
struct args_for_job_t *container;
container = (struct args_for_job_t *)arg;

/* A mock job */
printf("[TID - %d]\n", container->tid);

/* For the sake of returning something, not necessary */
return NULL;
}

int main ()
{
int return_code; /* Will hold return codes */
int tid; /* Thread id */
struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */

/* Initialize and set thread detached attribute */
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);

/* Spawn detached threads */
for (tid = 0; tid < MAX_THREADS; tid++)
{
args[tid].tid = tid;
return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid]));
if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; }
}

/* Free thread attribute */
pthread_attr_destroy(&tattr);

/* Delay main() completion until all detached threads finish their jobs. */
usleep(100000);
return EXIT_SUCCESS;
}

代码片段 1 的 Valgrind 输出(加入的线程和内存泄漏)

==27802== 
==27802== HEAP SUMMARY:
==27802== in use at exit: 1,558 bytes in 4 blocks
==27802== total heap usage: 105 allocs, 101 frees, 28,814 bytes allocated
==27802==
==27802== Searching for pointers to 4 not-freed blocks
==27802== Checked 104,360 bytes
==27802==
==27802== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==27802== by 0x400894D: _dl_map_object (dl-load.c:162)
==27802== by 0x401384A: dl_open_worker (dl-open.c:225)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x4013319: _dl_open (dl-open.c:639)
==27802== by 0x517F601: do_dlopen (dl-libc.c:89)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48)
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53)
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130)
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130)
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265)
==27802==
==27802== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==27802== by 0x400B7EC: _dl_new_object (dl-object.c:161)
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051)
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568)
==27802== by 0x401384A: dl_open_worker (dl-open.c:225)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x4013319: _dl_open (dl-open.c:639)
==27802== by 0x517F601: do_dlopen (dl-libc.c:89)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48)
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53)
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130)
==27802==
==27802== 312 bytes in 1 blocks are still reachable in loss record 3 of 4
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==27802== by 0x4010B59: _dl_check_map_versions (dl-version.c:300)
==27802== by 0x4013E1F: dl_open_worker (dl-open.c:268)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x4013319: _dl_open (dl-open.c:639)
==27802== by 0x517F601: do_dlopen (dl-libc.c:89)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48)
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53)
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130)
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130)
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265)
==27802==
==27802== 1,174 bytes in 1 blocks are still reachable in loss record 4 of 4
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==27802== by 0x400B57D: _dl_new_object (dl-object.c:77)
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051)
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568)
==27802== by 0x401384A: dl_open_worker (dl-open.c:225)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x4013319: _dl_open (dl-open.c:639)
==27802== by 0x517F601: do_dlopen (dl-libc.c:89)
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178)
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48)
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53)
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130)
==27802==
==27802== LEAK SUMMARY:
==27802== definitely lost: 0 bytes in 0 blocks
==27802== indirectly lost: 0 bytes in 0 blocks
==27802== possibly lost: 0 bytes in 0 blocks
==27802== still reachable: 1,558 bytes in 4 blocks
==27802== suppressed: 0 bytes in 0 blocks
==27802==
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
--27802--
--27802-- used_suppression: 2 dl-hack3-cond-1
==27802==
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

代码片段 1 的 Valgrind 输出(没有内存泄漏,稍后运行几次)

--29170-- Discarding syms at 0x64168d0-0x6426198 in /lib/x86_64-linux-gnu/libgcc_s.so.1 due to munmap()
==29170==
==29170== HEAP SUMMARY:
==29170== in use at exit: 0 bytes in 0 blocks
==29170== total heap usage: 105 allocs, 105 frees, 28,814 bytes allocated
==29170==
==29170== All heap blocks were freed -- no leaks are possible
==29170==
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
--29170--
--29170-- used_suppression: 2 dl-hack3-cond-1
==29170==
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

最佳答案

当你的线程被分离时,你有一个错误,导致未定义的行为。

在 main 中你有这行代码:

struct args_for_job_t args[MAX_THREADS];

您将指针交给您的工作线程。

然后main()就到了这部分

pthread_exit(NULL);

并且 main() 不再存在,但您仍然可能有工作线程,它访问上面的 args 数组,该数组位于 main() 的堆栈上 - 它不再存在。在某些运行中,您的工作线程可能会在 main() 结束之前全部完成,但在其他运行中则不会。

关于c pthreads + valgrind = 内存泄漏 : why?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15207762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com