问题描述
#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
解决方法
POSIX标准未指定pthread_t
对应的实际类型。在glibc中,它是unsigned long int
,这就是C ++流可以打印其值的原因。 (这不是便携式的。)
为什么pthread_self
返回零,但仅当不与-lpthread
链接时返回零?
从历史上看,glibc支持两种不同的线程库实现,即LinuxThreads和NPTL。一些核心libpthread
函数在glibc本身中具有存根,如果已加载存根,则存根会转发到实际的实现;如果进程中没有线程库,则存根会执行一些虚拟的回退操作。例如,pthread_mutex_lock
和pthread_mutex_unlock
的虚拟实现什么都不做。
对于pthread_self
,虚拟实现必须返回 some 值,并且该值为零。 (POSIX不会保留任何pthread_t
值来指示故障或其他极端情况,因此很好。)与-lpthread
链接时,使用非虚拟实现,该实现返回指向内部的指针。线程数据结构(转换为unsigned long
),并且该值永远不会为零,即使对于初始(主)线程也是如此。这就是为什么链接器命令行上-lpthread
的存在很重要的原因。
其他人无法重现此行为,因为他们正在使用非glibc系统或更新的glibc版本。如今,只有一种针对glibc的线程库实现,它始于NPTL,并且与其余glibc紧密集成。这意味着不再需要转发器,在glibc 2.27中,pthread_self
never returns zero。顺便说一句,此更改有助于实现std::thread
,其中get_id()
返回的值为零:std::thread::id{}
表示没有ID,而不是线程是主线程(如较早的glibc未加载libpthread
的版本)。 libstdc++
因此需要支持glibc 2.26和更早版本。
转发器还有另一个好处:它们不仅可以针对pthread_mutex_lock
进行性能优化,而且还可以优化性能(如果未链接libpthread
则不执行任何操作)。如果没有libpthread
,则某些-lpthread
功能可用。这意味着应用程序可以使用同步和,并且可以检查符号pthread_create
或pthread_cancel
等符号的存在(使用弱符号引用)。如果未定义这些弱符号,则很明显该进程必须是单线程的,因为libpthread
不存在,这意味着无法创建线程。在这种情况下,例如,不必使用原子指令进行引用计数。 (std::shared_ptr
中的libstdc++
使用了这样的优化。)但是这种优化的效率越来越低,因为如今大多数过程映像都包含libpthread
:某些库需要它,因此它被作为间接依赖项加载。 。这意味着转发器也失去了这个其他目的,它们正逐渐从glibc中删除。 (添加了__libc_single_threaded
的glibc 2.32启用了单线程优化,即使加载了libpthread
却没有创建线程。)