pthread_exit 与 return

发布于 2024-09-25 23:32:42 字数 1413 浏览 7 评论 0原文

我有一个可连接的 pthread 运行程序函数,定义如下:

void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}

该线程应该加入主线程。

每当我通过 Valgrind 运行程序时,我都会遇到以下泄漏

LEAK SUMMARY:
   definitely lost: 0 bytes in 0 blocks
   indirectly lost: 0 bytes in 0 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 968 bytes in 5 blocks
        suppressed: 0 bytes in 0 blocks

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)

我检查了 pthreads 的手册页,上面写着:

  The new thread terminates in one of the following ways:

   * It  calls  pthread_exit(3),  specifying  an exit status value that is
     available  to  another  thread  in  the  same  process   that   calls
     pthread_join(3).

   * It  returns  from  start_routine().   This  is  equivalent to calling
     pthread_exit(3) with the value supplied in the return statement.

   * It is canceled (see pthread_cancel(3)).

   * Any of the threads in the process calls exit(3), or the  main  thread
     performs  a  return  from main().  This causes the termination of all
     threads in the process.

奇迹般地,当我用 return 语句替换 pthread_exit() 时,泄漏消失了。

return(NULL);

我的实际问题是三个方面的:

  1. 有人可以解释为什么 return 语句没有泄漏吗?
  2. 就退出线程而言,这两个语句之间是否存在一些根本区别?
  3. 如果是这样,什么时候应该优先选择其中一个?

I have a joinable pthread runner function defined as below:

void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}

This thread is supposed to join the main thread.

Whenever I ran my program through Valgrind I would get the following leaks:

LEAK SUMMARY:
   definitely lost: 0 bytes in 0 blocks
   indirectly lost: 0 bytes in 0 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 968 bytes in 5 blocks
        suppressed: 0 bytes in 0 blocks

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)

I checked the man page for pthreads which said:

  The new thread terminates in one of the following ways:

   * It  calls  pthread_exit(3),  specifying  an exit status value that is
     available  to  another  thread  in  the  same  process   that   calls
     pthread_join(3).

   * It  returns  from  start_routine().   This  is  equivalent to calling
     pthread_exit(3) with the value supplied in the return statement.

   * It is canceled (see pthread_cancel(3)).

   * Any of the threads in the process calls exit(3), or the  main  thread
     performs  a  return  from main().  This causes the termination of all
     threads in the process.

Miraculously, when I replaced the pthread_exit() with a return statement, the leaks disappeared.

return(NULL);

My actual question is three-pronged:

  1. Can someone explain why the return statement gave no leaks?
  2. Is there some fundamental difference between both statements, in relation to exiting from threads?
  3. If so, when should one be preferred over the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

溺渁∝ 2024-10-02 23:32:42

以下最小测试用例展示了您所描述的行为:

#include <pthread.h>
#include <unistd.h>

void *app1(void *x)
{
    sleep(1);
    pthread_exit(0);
}

int main()
{
    pthread_t t1;

    pthread_create(&t1, NULL, app1, NULL);
    pthread_join(t1, NULL);

    return 0;
}

valgrind --leak-check=full --show-reachable=yes 显示了从 pthread_exit() 调用的函数分配的 5 个块> 未释放但在进程退出时仍可访问。如果将pthread_exit(0);替换为return 0;,则这5个块不会被分配。

但是,如果您测试创建和加入大量线程,您会发现退出时使用的未释放内存量并没有增加。这以及它仍然可以访问的事实表明您刚刚看到了 glibc 实现的奇怪之处。一些 glibc 函数在第一次调用时使用 malloc() 分配内存,并在进程生命周期的剩余时间内保持分配状态。 glibc 不会在进程退出时释放这些内存,因为它知道进程无论如何都会被拆除 - 这只是浪费 CPU 周期。

The following minimal test case exhibits the behaviour you describe:

#include <pthread.h>
#include <unistd.h>

void *app1(void *x)
{
    sleep(1);
    pthread_exit(0);
}

int main()
{
    pthread_t t1;

    pthread_create(&t1, NULL, app1, NULL);
    pthread_join(t1, NULL);

    return 0;
}

valgrind --leak-check=full --show-reachable=yes shows 5 blocks allocated from functions called by pthread_exit() that is unfreed but still reachable at process exit. If the pthread_exit(0); is replaced by return 0;, the 5 blocks are not allocated.

However, if you test creating and joining large numbers of threads, you will find that the amount of unfreed memory in use at exit does not increase. This, and the fact that it is still reachable, indicates that you're just seeing an oddity of the glibc implementation. Several glibc functions allocate memory with malloc() the first time they're called, which they keep allocated for the remainder of the process lifetime. glibc doesn't bother to free this memory at process exit, since it knows that the process is being torn down anyway - it'd just be a waste of CPU cycles.

饮湿 2024-10-02 23:32:42

不确定您是否仍然对此感兴趣,但我目前正在调试类似的情况。使用 pthread_exit 的线程会导致 valgrind 报告可到达的块。原因似乎在这里得到了很好的解释:

https://bugzilla.redhat.com/show_bug .cgi?id=483821

本质上,pthread_exit 似乎会导致 dlopen 在进程退出时永远不会被显式清理。

Not sure if you're still interested in this, but I am currently debugging a similar situation. Threads that use pthread_exit cause valgrind to report reachable blocks. The reason seems to be fairly well explained here:

https://bugzilla.redhat.com/show_bug.cgi?id=483821

Essentially it seems pthread_exit causes a dlopen which is never cleaned up explicitly when the process exits.

别挽留 2024-10-02 23:32:42

你真的在使用C++吗?澄清一下 - 您的源文件以 .c 扩展名结尾,并且您使用 gcc 编译它,而不是 g++

您的函数似乎很可能正在分配您希望在函数返回时自动清理的资源。本地 C++ 对象(例如 std::vectorstd::string)执行此操作,如果您调用 pthread_exit,它们的析构函数可能不会运行,但如果你回来的话就会被清理干净。

我的偏好是避免使用 pthread_exit 等低级 API,并且尽可能从线程函数返回。它们是等效的,除了 pthread_exit 是一个事实上的流控制构造,它绕过您正在使用的语言,但 return 不会。

Are you actually using C++, by any chance? To clarify - your source file ends with a .c extension, and you are compiling it with gcc, not g++?

It seems reasonably likely that your function is allocating resources that you expect to be cleaned up automatically when the function returns. Local C++ objects like std::vector or std::string do this, and their destructors probably won't be run if you call pthread_exit, but would be cleaned up if you just return.

My preference is to avoid low-level APIs such as pthread_exit, and always just return from the thread function, where possible. They're equivalent, except that pthread_exit is a de-facto flow-control construct that bypasses the language you're using, but return doesn't.

巾帼英雄 2024-10-02 23:32:42

看起来调用 exit() (显然,pthread_exit())会留下自动分配的变量。您必须返回或投掷才能正确放松。

根据 C++ valgrind STL 字符串可能存在泄漏

@Klaim:我不明白该文件哪里说我错了,但是如果
如果确实如此,那就错了。引用 C++ 标准 (§18.3/8):
“调用 exit() 不会破坏自动对象。” –
詹姆斯·麦克内利斯 2010-09-10 19:11

由于执行“return 0”而不是“pthread_exit(0)”似乎解决了您的问题(以及我的问题..谢谢),我假设两者之间的行为相似。

It looks like calling exit() (and, apparently, pthread_exit()) leaves automatically-allocated variables allocated. You must either return or throw in order to properly unwind.

Per C++ valgrind possible leaks on STL string:

@Klaim: I don't see where that document says that I am wrong, but if
it does then it is wrong. To quote the C++ standard (§18.3/8):
"Automatic objects are not destroyed as a result of calling exit()." –
James McNellis Sep 10 '10 at 19:11

Since doing a "return 0" instead of "pthread_exit(0)" seemed to solve your problem (and mine.. thanks), I'm assuming that the behavior is similar between the two.

夏末染殇 2024-10-02 23:32:42

Valgrind 很难跟踪 pthread_exit 变量,这就是为什么 pthread_exit 显示内存泄漏且标签仍然可达;但在 return 的情况下则不然。

it's difficult for Valgrind to track pthread_exit variables that's why with pthread_exit it shows memory leak with still reachable tag; but not in the case with return.

冬天的雪花 2024-10-02 23:32:42

我的经验是,valgrind 很难跟踪为可连接线程的状态分配的存储。 (这与 caf 指示的方向相同。)

由于您似乎总是返回 0 值,我猜您可能需要从应用程序的角度加入线程?如果是这样,请考虑从一开始就分离地启动它们,这可以避免分配该内存。

缺点是你要么:


  1. main 的末尾。如果您知道
    预先的线程数,a
    简单静态分配
    pthread_barrier 就可以了。
  2. 或者退出 main
    pthread_exit 这样你就不会
    杀死其余正在运行的线程
    这可能还没有完成。

I have the experience that valgrind has difficulties of tracking the storage that is allocated for the state of joinable threads. (This goes in the same direction as caf indicates.)

Since it seems that you always return a value of 0 I guess that you perhaps need to join your threads from an application point of view? If so consider of launching them detached from the start, this avoids the allocation of that memory.

The downside is that you either have:

  1. to implement your own barrier at the
    end of your main. If you know the
    number of threads beforehand, a
    simple statically allocated
    pthread_barrier would do.
  2. or to exit you main with
    pthread_exit such that you don't
    kill the rest of the running threads
    that might not yet be finished.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文