POSIX 线程库(简称 pthread)是 UNIX/Linux 系统下遵循 POSIX 标准 的多线程编程接口,提供了一套跨平台的线程创建、同步和管理机制。
1.线程创建
1.1.pthread_create
pthread_create()是 POSIX 线程库中用于创建新线程的核心函数,其功能类似于进程中的fork(),但更轻量级。

返回值:成功时返回 0,失败时返回错误码(非 errno,需用 strerror() 转换)。
参数解析
thread:输出参数,用于保存新线程的唯一标识符(线程ID)。
attr:线程属性(如栈大小、调度策略),传 NULL 表示使用默认属性。
start_routine:线程入口函数,格式必须为 void *func(void *args)。
arg:传递给 start_routine 的参数,需强制转换为 void* 类型。
传入的start_routine如果是成员函数则必须是静态成员函数:
- start_routine 参数的类型是 void* (*start_routine)(void*),即一个 “参数为 void*、返回值为 void* 的函数指针”。
- C++ 的非静态成员函数会隐式包含一个 this 指针(指向类实例自身),其函数签名void* (Thread<T>* this, void* args) 与 start_routine 不兼容
- 静态成员函数属于类本身,而非类的某个实例,它不包含 this 指针,函数签名为 void* Routine(void*),刚好匹配 pthread_create 对 start_routine 的要求
线程被创建好后,新线程要被主线程等待,类似僵尸进程的问题。
1.2.pthread_join
pthread_join 是 POSIX 线程库中用于线程同步和资源回收的关键函数,主要作用是阻塞当前线程,直到目标线程终止,并获取其返回值。

pthread_t thread:目标线程的ID(需等待的线程)
void **retval:输出参数:存储目标线程的返回值(可设为NULL)
成功返回 0,失败返回错误码(非 errno)。
#include <iostream>
#include <string>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
void showtid(pthread_t tid)
{
printf("tid: 0x%lx\n", tid);
}
void *routine(void *args)
{
std::string name = static_cast<const char*>(args);
int cnt = 5;
while(cnt--)
{
std::cout << "我是一个新线程:" << name << std::endl;
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, routine, (void*)"thread-1");
showtid(tid);
pthread_join(tid, nullptr);
return 0;
}

1.3.pthread_self
pthread_self() 是 POSIX 线程库中用于获取当前线程唯一标识符(线程ID)的函数,其功能类似于进程中的 getpid()。

#include <iostream>
#include <string>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
int flag = 1000;
void showtid(pthread_t tid)
{
printf("tid: 0x%lx\n", tid);
}
std::string FormatId(pthread_t id)
{
char tid[64];
snprintf(tid, sizeof(tid), "0x%lx", id);
return tid;
}
void *routine(void *args)
{
std::string name = static_cast<const char*>(args);
pthread_t id = pthread_self();
int cnt = 5;
while(cnt--)
{
sleep(1);
std::cout << "我是一个新线程:" << name << "我的id是:" << FormatId(id) << std::endl;
flag++;
}
return (void*)100;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, routine, (void*)"thread-1");
showtid(tid);
int cnt = 5;
while(cnt--)
{
std::cout << "我是主线程:" << "我的id'是:"
<< FormatId(pthread_self()) << ", flag: " << flag << std::endl;
sleep(1);
}
void *ret = nullptr;
pthread_join(tid, &ret);
std::cout << "ret: " << (long long)ret << std::endl;
return 0;
}

可以看到两个线程共享同一份资源
两个线程都调用了这个函数,这是一个可重入函数
std::string FormatId(pthread_t id)
{
char tid[64];
snprintf(tid, sizeof(tid), "0x%lx", id);
return tid;
}
给新线程传递的参数和返回值可以是任意类型
class Task
{
public:
Task(int a, int b)
: _a(a)
, _b(b)
{}
int Execute()
{
return _a + _b;
}
~Task(){}
private:
int _a;
int _b;
};
class Result
{
public:
Result(int result) : _result(result){}
int GetResult() { return _result;}
~Result(){}
private:
int _result;
};
void *routinue(void *args)
{
Task *t = static_cast<Task*>(args);
sleep(1);
Result *res = new Result(t->Execute());
sleep(1);
return res;
}
int main()
{
pthread_t tid;
Task *t = new Task(10, 20);
pthread_create(&tid, nullptr, routinue, (void*)t);
Result *ret = nullptr;
pthread_join(tid, (void **)&ret);
int n = ret->GetResult();
std::cout << "进程结束,退出码:" << n << std::endl;
delete t;
delete ret;
return 0;
}
![]()
2.线程终止
1. 从线程函数中 return。这种方法对主线程不适用,从 main 函数 return 相当于调用 exit。

参数等价于return void*
3. 一个线程可以调用 pthread_ cancel 终止同一进程中的另一个线程。

pthread_cancel 用来取消同一个进程中的线程
返回值:成功返回0,失败返回错误码
#include <iostream>
#include <string>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
class Task
{
public:
Task(int a, int b)
: _a(a)
, _b(b)
{}
int Execute()
{
return _a + _b;
}
~Task(){}
private:
int _a;
int _b;
};
class Result
{
public:
Result(int result) : _result(result){}
int GetResult() { return _result;}
~Result(){}
private:
int _result;
};
void *routinue(void *args)
{
Task *t = static_cast<Task*>(args);
sleep(100);
Result *res = new Result(t->Execute());
//return res;
pthread_exit(res); //与return res等价
}
int main()
{
pthread_t tid;
Task *t = new Task(10, 20);
pthread_create(&tid, nullptr, routinue, (void*)t);
sleep(3);
pthread_cancel(tid);
std::cout << "新线程被取消" << std::endl;
void *ret = nullptr;
pthread_join(tid, &ret);
std::cout << "新线程结束,退出码:" << (long long)ret << std::endl;
return 0;
}

线程如果被退出,退出结果是-1
![]()
thread 线程以不同的方式终止,通过 pthread_join 得到的终止状态是不同的,总结如下:
1. 如果 thread 线程通过 return 返回,value_ptr 所指向的单元里存放的是 thread 线程函数的返回值。
2. 如果 thread 线程被别的线程调用 pthread_cancel 异常终止,value_ptr 所指向的单元里存放的是常数 PTHREAD_CANCELED。
3. 如果 thread 线程是自己调用 pthread_exit 终止的,value_ptr 所指向的单元存放的是传给 pthread_exit 的参数。
4. 如果对 thread 线程的终止状态不感兴趣,可以传 NULL 给 value_ptr 参数。
3.分离线程(pthread_detach)
默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行 pthread_join 操作,否则,
无法释放资源,从而造成系统泄漏。 如果不关心线程的返回值,join是一种负担,这个时候,我们
将线程设置为分离状态,当线程退出时,自动释放线程资源。
pthread_detach 用于将线程设置为 分离状态(detached),使得线程结束时自动释放资源,而无需调用 pthread_join。

void *routinue(void *args)
{
pthread_detach(pthread_self());
std::cout << "线程被分离" << std::endl;
int cnt = 5;
while(cnt--)
{
sleep(1);
std::cout << "我是新进程" << std::endl;
}
return nullptr;
}
int main()
{
pthread_t tid;
Task *t = new Task(10, 20);
pthread_create(&tid, nullptr, routinue, (void*)t);
int cnt = 5;
while(cnt--)
{
sleep(1);
std::cout << "我是主进程" << std::endl;
}
int n = pthread_join(tid, nullptr);
if(n == 0)
{
std::cout << "pthread_join sucess: " << std::endl;
}else
{
std::cout << "pthread_join fail: " << std::endl;
}
return 0;
}

4.设置线程名称
(1)pthread_setname_np(设置线程名称)
功能:为指定线程设置系统可见的名称。
int pthread_setname_np(pthread_t thread, const char *name);
thread:要设置名称的线程句柄(pthread_t 类型)。
name:线程名称,长度限制通常为 16 个字符(含终止符 \0),超出会被截断。
返回值:成功返回 0,失败返回错误码。
作用:设置后可通过 top -H、ps -T、gdb 等工具查看线程名称,方便调试多线程程序。
(2)pthread_getname_np(获取线程名称)
功能:获取指定线程的名称。
int pthread_getname_np(pthread_t thread, char *name, size_t len);
name:用于存储线程名称的字符数组。
len:name 数组的长度(需保证至少 16 字节以容纳名称)。
返回值:成功返回 0,失败返回错误码。
5.用户态线程(pthread)与 struct pthread
- struct pthread(TCB,Thread Control Block)
- 是 pthread 库在 用户态 维护的线程管理结构,包含:
struct pthread {
void* ret; 线程返回值(通过 return 或 pthread_exit 写入)
void* stack; 线程独立栈的地址
size_t stack_size; 栈大小
pthread_attr_t attr; 线程属性(如分离状态、调度策略)
pid_t tid; 内核 LWP(通过 gettid() 获取)
其他状态(如取消标志、锁等)
};

- pthread_create 会动态分配 struct pthread 对象,并返回其地址(即 pthread_t,本质是用户态句柄)。使用 pthread_create,会在库中创建TCB,在内核中创建轻量级进程(调用系统调用clone)
- 线程退出时
- struct pthread 也就是TCB,有一个属性void *(ret),当线程执行 return 或调用 pthread_exit()时,就会把返回值写入 ret 中。
- 此时线程虽然执行结束,但这个数据块并没有被释放,所以需要 pthread_join,并且需要传入该线程的 tid 作为参数已找到该线程对应的数据块
6. 内核态线程(LWP)与 task_struct
clone() 系统调用:pthread_create 底层通过 clone() 创建 内核线程(LWP)。

clone(
fn:线程函数
stack:用户态栈地址
flags:CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD,
args:传递给线程的参数
);
CLONE_THREAD:新线程共享相同的 PID(属于同一线程组).
CLONE_VM:共享地址空间(同一进程)。
task_struct(PCB):内核为每个 LWP 维护一个 `task_struct`,包含:
struct task_struct {
pid_t pid; // 线程组 ID(用户态看到的 PID)
pid_t tgid; // 等同于 pid(主线程的 PID)
pid_t tid; // 内核 LWP(唯一线程 ID)
struct mm_struct *mm; // 内存管理(共享同一进程的 mm)
// 调度相关:时间片、优先级、上下文(registers、FPU state)
};
pthread_t vs LWP
概念 用户态(pthread 库) 内核态(Linux 内核)
线程标识符 pthread_t(TCB 地址) tid(LWP,通过 gettid() 获取)
调度单位 无(依赖内核 LWP) task_struct(LWP)
栈管理 用户态分配栈(stack 属性) 内核映射用户栈到进程地址空间
线程创建流程:
1. 用户调用 pthread_create。
2. pthread 库分配 struct pthread(TCB)和用户栈。
3. 调用 clone() 创建内核线程(LWP),共享进程地址空间。
4. 新线程从用户态启动函数(fn)开始执行。
线程退出流程:
1. 线程调用 return 或 pthread_exit,返回值写入 TCB 的 ret。
2. 内核线程(LWP)退出,但 struct pthread 仍保留(供 pthread_join 读取)。
3. pthread_join 回收 TCB 和用户栈资源。
pthread_t 到底是什么?
- 在 glibc 中,pthread_t 是 struct pthread(用户态 TCB )的地址。
- 其他实现(如 musl libc)可能直接使用 tid(LWP),但 Linux 主流实现是 TCB 地址。
总结
- 用户态 pthread 库 管理 struct pthread(TCB),负责线程创建、属性、返回值等。
- 内核态 task_struct 管理 LWP,负责调度、时间片、上下文切换。
- pthread_t 是用户态句柄,LWP 是内核调度单位,二者通过 clone() 协作。
- 务必调用 pthread_join 或 pthread_detach 避免资源泄漏!
7.独立栈
1. 主线程(进程)的栈
- 分配方式:
- 通过 fork() 创建时,继承父进程的栈空间地址,采用 写时拷贝(CoW)机制。
- 栈空间可动态增长(由内核自动扩展),直至达到上限(默认通常 8MB)。
- 溢出行为:
- 访问未映射的栈地址时,内核尝试扩展栈;若超出上限则触发 段错误(SIGSEGV)。
- 特点:
- 向下增长:栈指针从高地址向低地址移动。
- 唯一允许“试探性”访问未映射页而不立即报错的内存区域(直到触及硬限制)。
2. 子线程(pthread)的栈
- 分配方式:
- 通过 pthread_create() 创建时,由 glibc 调用 mmap 在进程的 文件映射区(共享区)分配固定大小的栈空间。
mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
- 默认大小通常为 8MB,可通过 pthread_attr_setstacksize() 自定义。
- 溢出行为:
- 不可动态增长:栈耗尽时直接访问非法内存,立即触发段错误。
- 特点:
- 固定大小:由 mmap 预先分配,无 CoW 机制。
- 非严格私有:虽为线程私有,但因共享进程地址空间,其他线程可通过指针非法访问(需同步控制)。独立的栈,是指其他线程不知道该栈的地址,但是可以访问里面的内容,因为共享地址空间。
线程独立的上下文:有独立的PCB(内核)+ TCB(用户层,pthread库内部)
独立的栈:每个线程线程都有自己独立的栈,要么是进程的,要么是库中创建进程是mmap申请出来的

被折叠的 条评论
为什么被折叠?



