首页 > 解决方案 > pthread_self 返回一个大整数或 0,具体取决于 libpthread 是否存在

问题描述

#include <pthread.h>
#include <iostream>
 int main(){
     std::cout<<pthread_self()<<std::endl;
}

使用命令编译g++ 1.cpp -o 1并执行时,程序输出为0,如果使用命令编译,则g++ 1.cpp -o 1 -lpthread输出为大数,如 140062699310912。

我不明白为什么。

# g++ 1.cpp -o 1 && ./1
0
# g++ 1.cpp -o 1 -lpthread && ./1
140268785461056

标签: linuxpthreadsglibc

解决方案


POSIX 标准没有指定pthread_t对应的实际类型。在 glibc 中,它是unsigned long int,这就是 C++ 流可以打印其值的原因。(这不是便携式的。)

为什么pthread_self返回零,但仅在与 链接时-lpthread

从历史上看,glibc 支持两种不同的线程库实现,LinuxThreads 和 NPTL。一些核心libpthread函数在 glibc 本身中有存根,如果有任何加载,则转发到实际实现,或者如果进程中不存在线程库,则执行一些虚拟回退操作。例如,pthread_mutex_lockand的 dummy 实现pthread_mutex_unlock什么都不做。

在 的情况下pthread_self,虚拟实现必须返回一些值,并且该值为零。(POSIX 不保留任何pthread_t值来指示失败或其他极端情况,所以这很好。)当与 链接时-lpthread,使用非虚拟实现,它返回一个指向内部线程数据结构的指针(转换为unsigned long),并且这个value 永远不会为零,即使对于初始(主)线程也是如此。这就是为什么-lpthread链接器命令行上的存在很重要。

其他人无法重现此行为,因为他们使用的是非 glibc 系统或更新版本的 glibc。现在,glibc 只有一个线程库实现,最初是 NPTL,它与 glibc 的其余部分紧密集成。这意味着不再需要转发器,并且在 glibc 2.27 中,pthread_self永远不会返回零。顺便说一句,这个更改有助于实现,其中:std::thread返回的值零表示没有 ID,而不是线程是主线程(如在未加载的旧 glibc 版本中)。因此需要一个 kludge 来支持 glibc 2.26 及更早版本。get_id()std::thread::id{}libpthreadlibstdc++

转发器还有另一个好处:它们可以优化性能,而不仅仅是为了(如果未链接pthread_mutex_lock则什么都不做)。libpthread某些libpthread功能无需-lpthread. 这意味着应用程序可以使用同步,并且它们可以检查符号的存在,例如pthread_createpthread_cancel(使用弱符号引用)。如果这些弱符号是未定义的,那么很明显该进程必须是单线程的,因为libpthread不存在,这意味着不能创建线程。在这种情况下,例如,没有必要使用原子指令进行引用计数。(std::shared_ptrlibstdc++使用这样的优化。)但是这种优化越来越无效,因为大多数过程映像都包含libpthread这些天:某些库需要它,因此它作为间接依赖项加载。这意味着转发器也失去了其他目的,它们正逐渐从 glibc 中删除。(添加了glibc 2.32__libc_single_threaded以启用单线程优化,即使libpthread已加载,但尚未创建线程。)


推荐阅读