
🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!
🎬 博主简介:

文章目录
- 前言:
- [一. 进程 VS 线程:资源共享与私有划分](#一. 进程 VS 线程:资源共享与私有划分)
-
- [1.1 核心定位再梳理(整体图示解析)](#1.1 核心定位再梳理(整体图示解析))
- [1.2 线程间共享的进程资源](#1.2 线程间共享的进程资源)
- [1.3 线程私有的独立资源](#1.3 线程私有的独立资源)
- [1.4 关键问题:为什么一个线程崩溃,整个进程都会退出?](#1.4 关键问题:为什么一个线程崩溃,整个进程都会退出?)
- [二. Linux 线程控制:从创建到回收全流程](#二. Linux 线程控制:从创建到回收全流程)
-
- [2.1 前置说明:pthread 线程库](#2.1 前置说明:pthread 线程库)
- [2.2 线程创建:pthread_create](#2.2 线程创建:pthread_create)
- [2.3 多线程创建 -- 构建任务(等到线程等待结束还有一个优化)](#2.3 多线程创建 -- 构建任务(等到线程等待结束还有一个优化))
- [2.4 线程终止:3 种合法终止方式](#2.4 线程终止:3 种合法终止方式)
-
- [2.4.1 方式 1:线程入口函数 return 返回](#2.4.1 方式 1:线程入口函数 return 返回)
- [2.4.2 方式 2:pthread_exit:线程主动终止自身](#2.4.2 方式 2:pthread_exit:线程主动终止自身)
- [2.4.3 方式 3:pthread_cancel:终止同进程的其他线程](#2.4.3 方式 3:pthread_cancel:终止同进程的其他线程)
- [2.5 线程等待:pthread_join(附加:指针和指针变量辨析)](#2.5 线程等待:pthread_join(附加:指针和指针变量辨析))
- [2.6 多线程的优化写法(加上线程等待)](#2.6 多线程的优化写法(加上线程等待))
- [2.7 线程分离:pthread_detach](#2.7 线程分离:pthread_detach)
- [三. 补充:LWP 与原生线程库的封装关系](#三. 补充:LWP 与原生线程库的封装关系)
- 结尾:
前言:
大家好,我是深耕 Linux 内核与开发的学习者,上一篇我们吃透了线程的本质概念与内核实现,本篇覆盖**「进程 VS 线程资源划分」+「Linux 线程控制」**,资源共享 / 独占的底层逻辑,到线程创建、终止、等待、分离的全流程实战,知识点无遗漏、代码可直接运行,兼顾学习理解与面试复习。
上一篇我们明确了核心定义:
- 进程是操作系统资源分配的基本单位
- 线程是 CPU 调度的最小单位,是进程内部的执行分支,Linux 下本质是轻量级进程(LWP)
而想要真正用好线程,必须先搞懂两个核心问题:
- 同进程内的线程,哪些资源共享?哪些资源私有?
- 如何通过系统接口,完成线程的全生命周期控制?
本篇就围绕这两个问题,把知识点讲透、代码写全。
一. 进程 VS 线程:资源共享与私有划分
线程的 "轻量化",本质是 共享了进程的绝大部分资源,仅私有运行必需的最小上下文。我们先把共享与私有资源划清,这是理解线程所有特性的基础。
1.1 核心定位再梳理(整体图示解析)
- 进程 = 内核数据结构(task_struct/mm_struct 等) + 私有的代码和数据,承担系统资源分配
- 线程 = 进程内部的执行分支,粒度更细、更轻,仅需维护自身运行上下文
- 内核通过
task_struct统一管理进程和线程,线程与进程的核心差异,就在于资源是否共享。


1.2 线程间共享的进程资源
同进程内的所有线程,共享进程地址空间内的绝大部分资源,无需额外申请,这也是线程创建 / 切换成本极低的核心原因:
- 地址空间核心段:代码段(Text Segment)、数据段(Data Segment)、堆区、共享区
- 全局变量、静态变量、堆内申请的内存,所有线程都可直接访问修改
- 文件系统相关:文件描述符表、当前工作目录、用户 id / 组 id
- 一个线程打开的文件,其他线程可直接读写
- 信号相关:每种信号的处理方式(SIG_IGN/SIG_DFL/ 自定义处理函数)
- 信号是发给进程的,任意线程收到信号,都会触发整个进程的信号处理函数
- 进程内核数据:页表、mm_struct 内存描述符、vm_area_struct 内存区域结构
1.3 线程私有的独立资源
线程要独立被 CPU 调度执行,必须私有运行时的上下文数据,这些资源线程间完全隔离,互不影响:
- 线程 ID(TID):用户态 pthread 库的唯一标识,内核态对应 LWP 号
- 寄存器上下文:CPU 寄存器的值,线程切换时保存 / 恢复,保证执行流不混乱
- 独立栈空间:每个线程有自己的私有栈,存放局部变量、函数调用栈帧,互不干扰
- errno 变量:系统调用错误码,线程私有,避免多线程间错误码互相覆盖
- 信号屏蔽字:每个线程可独立设置信号屏蔽规则,不影响其他线程
- 调度优先级:线程可独立设置调度优先级,由内核单独调度
- 线程局部存储(TLS):线程私有的全局变量,仅当前线程可访问
1.4 关键问题:为什么一个线程崩溃,整个进程都会退出?
这是面试高频考点,结合资源划分就能瞬间理解:
- 线程触发异常(除零、野指针、非法内存访问),内核会向进程发送致命信号,而非单独发给线程;
- 信号的处理方式是进程级共享的,进程收到致命信号后,会直接终止整个进程;
- 进程终止后,地址空间、文件描述符等所有资源都会被回收,所有线程自然随之退出。

上篇博客中提到过,更详细的解析可以去上篇博客中看看
二. Linux 线程控制:从创建到回收全流程
Linux 内核仅提供轻量级进程的创建能力,我们日常使用的线程接口,都来自POSIX 标准的 pthread 线程库 ,所有接口都以pthread_开头,编译时必须通过-lpthread链接线程库。
2.1 前置说明:pthread 线程库
- 头文件:必须包含
<pthread.h> - 编译命令:
gcc xxx.c -o xxx -lpthread - 核心特点:线程库运行在用户态,负责线程的管理(TCB 线程控制块、线程栈分配),底层通过
clone系统调用让内核创建 LWP(其实我们之前学习的fork也是这样的)。

- 代码演示:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <sys/wait.h>
#include <signal.h>
// 子线程函数(LWP执行的函数)
int thread_func(void *arg)
{
int thread_id = *(int *)arg;
int count = 0;
printf("LWP %d (PID: %d, 父进程: %d) 开始执行\n",
thread_id, getpid(), getppid());
for (int i = 1; i <= 5; i++)
{
printf("LWP %d 计数: %d\n", thread_id, i);
sleep(1);
}
printf("LWP %d 执行完毕\n", thread_id);
return thread_id * 10; // 返回退出码
}
int main()
{
const int NUM_THREADS = 5;
void **stacks; // 栈空间指针数组
int *thread_ids; // 线程ID数组
pid_t *pids; // LWP的PID数组
int stack_size = 1024 * 1024; // 每个栈1MB
// 分配内存
stacks = (void **)malloc(NUM_THREADS * sizeof(void *));
thread_ids = (int *)malloc(NUM_THREADS * sizeof(int));
pids = (pid_t *)malloc(NUM_THREADS * sizeof(pid_t));
if (!stacks || !thread_ids || !pids)
{
perror("malloc失败");
exit(1);
}
printf("父进程 PID: %d\n\n", getpid());
// 创建5个LWP
for (int i = 0; i < NUM_THREADS; i++)
{
// 为每个LWP分配独立的栈空间(向下增长,分配在堆上)
stacks[i] = malloc(stack_size);
if (!stacks[i])
{
perror("栈分配失败");
exit(1);
}
thread_ids[i] = i + 1;
// 使用clone创建LWP
// CLONE_VM: 共享内存空间(父子进程共享地址空间)
// CLONE_FS: 共享文件系统信息
// CLONE_FILES: 共享文件描述符表
// CLONE_SIGHAND: 共享信号处理器
// CLONE_THREAD: 放入同一线程组(与父进程共享PID)
// SIGCHLD: 子进程退出时发送信号
pids[i] = clone(thread_func,
stacks[i] + stack_size, // 栈顶(栈向下增长)
CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND | CLONE_THREAD | SIGCHLD,
&thread_ids[i]);
if (pids[i] == -1)
{
perror("clone失败");
exit(1);
}
printf("创建 LWP %d, LWP ID: %d\n", thread_ids[i], pids[i]);
}
printf("\n所有LWP已创建, 等待执行完成...\n");
printf("注意:由于共享内存,主进程需要等待一段时间\n\n");
// 注意:因为使用了CLONE_THREAD,不能使用waitpid等待
// 简单的做法是等待足够长时间让子线程完成
sleep(8);
printf("\n主进程等待结束, 清理资源\n");
// 清理栈空间
for (int i = 0; i < NUM_THREADS; i++)
{
free(stacks[i]);
}
free(stacks);
free(thread_ids);
free(pids);
return 0;
}



2.2 线程创建:pthread_create
用于创建一个新的用户态线程,是线程控制最基础的接口。
函数原型:
cpp
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
参数详解:
| 参数 | 作用说明 |
|---|---|
thread |
输出型参数,用于返回新创建线程的用户态线程 ID |
attr |
线程属性设置,传 NULL 表示使用默认属性(栈大小、调度策略等) |
start_routine |
函数指针,线程的入口执行函数,线程创建成功后,会从这个函数开始执行 |
arg |
传递给线程入口函数的参数 |
返回值:
-
成功:返回 0
-
失败:直接返回错误码(不会设置 errno),无需通过 perror 打印,用
strerror(ret)解析错误信息 -
图示中的测试代码:
cpp
#include <iostream>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <pthread.h>
// int g_val = 100;
// int *p = nullptr;
// void hello(const std::string &name) {
// printf("haha, I am common function!, %s\n", name.c_str());
// sleep(5);
// }
class Task
{
public:
Task() = default;
void operator()()
{
}
~Task() = default;
private:
};
void *threaddrun1(void *args)
{
// p = (int*)malloc(sizeof(int) * 10);
std::string threadname = static_cast<const char*>(args);
while(true)
{
sleep(1);
std::cout << threadname << std::endl;
// printf("%s is running, g_val: %d, &g_val: %p\n", threadname.c_str(), g_val, &g_val);
// sleep(1);
// hello(threadname);
}
}
void* threaddrun2(void *args)
{
std::string threadname = static_cast<const char*>(args);
while(true)
{
sleep(1);
std::cout << threadname << std::endl;
// printf("%s is running, g_val: %d, &g_val: %p\n", threadname.c_str(), g_val, &g_val);
// sleep(1);
// g_val++;
// hello(threadname);
}
}
int main()
{
pthread_t t1, t2;
// 这里也可以定义类 Task t; -- 然后传进去
pthread_create(&t1, nullptr, threaddrun1, (void*)"thread-1");
pthread_create(&t2, nullptr, threaddrun2, (void*)"thread-2");
// pthread_join(t1, nullptr);
// pthread_join(t2, nullptr);
while(true)
{
// printf("Main thread, thread1 id: %ld, thread2 id: %ld\n", t1, t2);
printf("Main thread, thread1 id: %p, thread2 id: %p\n", t1, t2);
sleep(1);
}
return 0;
}



配套函数:pthread_self
cpp
pthread_t pthread_self(void);
功能:获取当前线程的用户态线程 ID,和进程的getpid()作用一致。
完整实战代码:创建线程
cpp
// 测试线程创建
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
while(true)
{
printf("我是一个新线程, tid: %lu, pid: %d\n", pthread_self(), getpid());
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, threadrun, (void*)"thread-1");
while(true)
{
printf("创建新进程成功, new tid: %lu, main tid: %lu, pid: %d\n", tid, pthread_self(), getpid());
sleep(1);
}
return 0;
}

关键说明:
- 线程的执行顺序由内核调度决定,主线程和新线程谁先执行不固定;
- 用户态的
pthread_t线程 ID,本质是进程地址空间中线程 TCB 结构体的地址,和内核 LWP 号不是一个概念; - 主线程如果提前退出(调用 exit/return),整个进程会终止,所有线程都会被强制退出。
2.3 多线程创建 -- 构建任务(等到线程等待结束还有一个优化)
Task.hpp
cpp
#pragma once
#include <iostream>
#include <string>
class Task
{
public:
Task(const std::string &who, int x, int y):_x(x), _y(y), _who(who)
{}
Task()
{}
void operator()()
{
std::cout << _who << " execute task: " <<_x << " + " << _y << " = " << _x + _y << std::endl;
}
~Task()
{}
private:
int _x;
int _y;
std::string _who;
};
testThread.cc
cpp
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include <vector>
int g_size = 64;
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
delete[] (char*)arg;
while(true)
{
printf("我是一个新线程, tid: %lu, pid: %d, name: %s\n", pthread_self(), getpid(), name.c_str());
sleep(1);
}
return nullptr;
}
// ./CreateThread threadnum;
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: %s threadnum\n", argv[0]);
return -1;
}
int threadnum = std::stoi(argv[1]);
std::vector<pthread_t> tids;
for(int i = 0; i < threadnum; i++)
{
pthread_t tid;
// char threadname[g_size]; // 这个是不太行的, 如果要让每个线程都有自己的名称, 则需要动态分配内存, 不能使用栈内存
char* threadname = (char*)malloc(g_size);
snprintf(threadname, g_size, "thread-%d", i + 1);
pthread_create(&tid, NULL, threadrun, (void*)threadname);
// sleep(1); 这个是不可以完全解决这个问题的
tids.push_back(tid);
}
sleep(10);
for(auto& tid : tids)
{
printf("main for 创建新进程成功, new tid: %lu, main tid: %lu, pid: %d\n", tid, pthread_self(), getpid());
}
// 主线程
while(true)
{
std::cout << "main thread is running..." << std::endl;
sleep(1);
}
return 0;
}


任务构建版:
cpp
// 构建一个任务进行测试
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include "Task.hpp"
int g_size = 64;
void* threadrun(void* arg)
{
Task *t = static_cast<Task*>(arg);
sleep(1);
(*t)();
sleep(1);
while(true)
{
sleep(1);
// printf("我是一个新线程, tid: %lu, pid: %d, name: %s\n", pthread_self(), getpid(), name.c_str());
// sleep(1);
}
return nullptr;
}
// ./CreateThread threadnum;
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: %s threadnum\n", argv[0]);
return -1;
}
int threadnum = std::stoi(argv[1]);
std::vector<pthread_t> tids;
for(int i = 0; i < threadnum; i++)
{
pthread_t tid;
char threadname[g_size]; // 这个是不太行的, 如果要让每个线程都有自己的名称, 则需要动态分配内存, 不能使用栈内存
// char* threadname = (char*)malloc(g_size);
snprintf(threadname, g_size, "thread-%d", i + 1);
Task *t = new Task(threadname, 10 + i, 20 * i);
pthread_create(&tid, NULL, threadrun, (void*)t);
tids.push_back(tid);
sleep(1);
}
sleep(10);
for(auto& tid : tids)
{
printf("main for 创建新进程成功, new tid: %lu, main tid: %lu, pid: %d\n", tid, pthread_self(), getpid());
}
// 主线程
while(true)
{
std::cout << "main thread is running..." << std::endl;
sleep(1);
}
return 0;
}


2.4 线程终止:3 种合法终止方式
如果需要只终止某个线程,而不终止整个进程,有 3 种安全合法的方式,严禁在线程内调用 exit (),会导致整个进程退出。
2.4.1 方式 1:线程入口函数 return 返回
最推荐的方式,线程入口函数执行完毕 return,线程自动终止,返回值可被pthread_join获取。
cpp
// 测试线程退出
// 1. return 退出线程函数
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
const int g_size = 64;
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
int cnt = 5;
while(cnt)
{
printf("我是一个新进程: tid: 0x%lx, pid: %d, name: %s, cnt: %d\n",
pthread_self(), getpid(), name.c_str(), cnt);
cnt--;
sleep(1);
return nullptr;
}
// 新进程退出
// 只要自己的线程函数跑完,线程自然退出了 -- 调用return,表示线程退出
// return nullptr;
}
int main()
{
pthread_t tid;
char threadname[g_size];
snprintf(threadname, g_size, "thread-%d", 1);
pthread_create(&tid, NULL, threadrun, (void*)threadname);
while(true)
pause();
return 0;
}
2.4.2 方式 2:pthread_exit:线程主动终止自身
线程内调用该函数,主动终止自身,效果和 return 一致,返回值可被pthread_join获取。
函数原型
cpp
void pthread_exit(void *value_ptr);
- 参数
value_ptr:线程退出的返回值,不能指向线程栈内的局部变量; - 无返回值,调用后线程直接终止,不会再返回。
代码示例:
cpp
// 2. pthread_exit() 退出线程函数
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
const int g_size = 64;
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
int cnt = 5;
while(cnt)
{
printf("我是一个新进程: tid: 0x%lx, pid: %d, name: %s, cnt: %d\n",
pthread_self(), getpid(), name.c_str(), cnt);
cnt--;
sleep(1);
// pthread_exit(nullptr);
}
// 新进程退出
pthread_exit(nullptr);
return nullptr;
}
int main()
{
pthread_t tid;
char threadname[g_size];
snprintf(threadname, g_size, "thread-%d", 1);
pthread_create(&tid, NULL, threadrun, (void*)threadname);
// pthread_exit(nullptr);
while(true)
pause();
return 0;
}

2.4.3 方式 3:pthread_cancel:终止同进程的其他线程
一个线程调用该函数,取消同一个进程内的另一个线程,被取消的线程默认退出码是PTHREAD_CANCELED(值为 - 1)。
函数原型
cpp
int pthread_cancel(pthread_t thread);
- 参数
thread:要终止的目标线程 ID; - 返回值:成功返回 0,失败返回错误码。
代码示例:
cpp
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
const int g_size = 64;
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
int cnt = 5;
while(cnt)
{
printf("我是一个新进程: tid: 0x%lx, pid: %d, name: %s, cnt: %d\n",
pthread_self(), getpid(), name.c_str(), cnt);
cnt--;
sleep(1);
// pthread_cancel(pthread_self()); // 自己取消自己
}
return (void*)10;
}
int main()
{
pthread_t tid;
char threadname[g_size];
snprintf(threadname, g_size, "thread-%d", 1);
pthread_create(&tid, NULL, threadrun, (void*)threadname);
int n = pthread_cancel(tid);
if(n != 0)
{
printf("pthread_cancel error: %d\n", n);
}
else
{
printf("pthread_cancel success\n");
}
void* ret = nullptr;
pthread_join(tid, &ret);
printf("join 0x%lx success, ret code: %lld\n", tid, (long long)ret);
while(true)
pause();
return 0;
}


2.5 线程等待:pthread_join(附加:指针和指针变量辨析)
线程退出后,其资源不会被自动释放,必须通过pthread_join等待线程退出,回收资源,否则会造成内存泄漏。
函数原型
cpp
int pthread_join(pthread_t thread, void **value_ptr);
参数详解
thread:要等待的目标线程 ID;value_ptr:输出型参数,用于接收线程的退出返回值。如果不关心退出码,传 NULL 即可。
返回值
- 成功:返回 0
- 失败:返回错误码
线程退出码的 3 种情况
- 线程通过
return返回:value_ptr指向的是线程return的返回值; - 线程通过
pthread_exit终止:value_ptr指向的是pthread_exit传入的参数; - 线程被
pthread_cancel取消:value_ptr指向的是常数PTHREAD_CANCELED。
完整实战代码:线程等待全场景
cpp
// 测试线程等待
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
const int g_size = 64;
void* threadrun(void* arg)
{
std::string name = static_cast<const char*>(arg);
int cnt = 5;
while(cnt)
{
printf("我是一个新进程: tid: 0x%lx, pid: %d, name: %s, cnt: %d\n",
pthread_self(), getpid(), name.c_str(), cnt);
cnt--;
sleep(1);
}
// return (void*)10;
pthread_exit((void*)100); // 再试试这个
}
int main()
{
pthread_t tid;
char threadname[g_size];
snprintf(threadname, g_size, "thread-%d", 1);
pthread_create(&tid, NULL, threadrun, (void*)threadname);
void* ret = nullptr;
pthread_join(tid, &ret);
printf("join 0x%lx success, ret code: %lld\n", tid, (long long)ret);
while(true)
pause();
return 0;
}




2.6 多线程的优化写法(加上线程等待)
Task.hpp
cpp
#pragma once
#include <iostream>
#include <string>
class Task
{
public:
Task(const std::string &who, int x, int y):_x(x), _y(y), _who(who)
{}
Task()
{}
void Execute()
{
_result = _x + _y;
}
std::string GetResult()
{
return std::to_string(_x) + " + " + std::to_string(_y) + " = " + std::to_string(_result);
}
~Task()
{}
private:
int _x;
int _y;
int _result;
std::string _who;
};
testThread.cc
cpp
// 多线程的优化
#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include "Task.hpp"
int g_size = 64;
void* threadrun(void* arg)
{
Task *t = static_cast<Task*>(arg);
t->Execute();
return t;
}
// ./CreateThread threadnum;
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: %s threadnum\n", argv[0]);
return -1;
}
int threadnum = std::stoi(argv[1]);
std::vector<pthread_t> tids;
for(int i = 0; i < threadnum; i++)
{
pthread_t tid;
char threadname[g_size]; // 这个是不太行的, 如果要让每个线程都有自己的名称, 则需要动态分配内存, 不能使用栈内存
// char* threadname = (char*)malloc(g_size);
snprintf(threadname, g_size, "thread-%d", i + 1);
Task *t = new Task(threadname, 10 + i, 20 * i);
pthread_create(&tid, NULL, threadrun, (void*)t);
tids.push_back(tid);
std::cout << "create thread " << threadname << " success, tid: " << tid << std::endl;
// sleep(1);
}
// 等待所有线程执行完成
std::vector<Task*> result_list;
for(auto& tid : tids)
{
Task *t = nullptr;
pthread_join(tid, (void**)&t);
result_list.push_back(t);
std::cout << "join success: " << tid << std::endl;
}
// 处理结果清单
std::cout << "result list: " << std::endl;
for(auto& t : result_list)
{
std::cout << t->GetResult() << std::endl;
}
return 0;
}



2.7 线程分离:pthread_detach
默认情况下,线程是joinable状态,退出后必须通过pthread_join回收资源。如果我们不关心线程的返回值,不想阻塞等待线程退出,可以将线程设置为分离状态,线程退出后,系统会自动回收其资源,无需手动 join。
函数原型
cpp
int pthread_detach(pthread_t thread);
- 参数
thread:要分离的目标线程 ID; - 返回值:成功返回 0,失败返回错误码。
使用方式
- 其他线程分离:主线程创建子线程后,调用
pthread_detach(tid)分离子线程; - 线程自分离:子线程内调用
pthread_detach(pthread_self())分离自身。
完整实战代码:线程分离
cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
// 线程自分离示例
void* thread_detach_self(void* arg) {
// 线程自分离
pthread_detach(pthread_self());
printf("子线程:已完成自分离,运行3秒后退出\n");
sleep(3);
printf("子线程:退出,系统自动回收资源\n");
pthread_exit(NULL);
}
int main() {
pthread_t tid;
int ret = pthread_create(&tid, NULL, thread_detach_self, NULL);
if (ret != 0) {
fprintf(stderr, "创建线程失败:%s\n", strerror(ret));
exit(1);
}
printf("主线程:子线程已创建,无需join等待\n");
// 尝试join分离的线程,会直接失败
ret = pthread_join(tid, NULL);
if (ret != 0) {
fprintf(stderr, "join分离线程失败:%s\n", strerror(ret));
}
// 主线程等待子线程退出,否则进程退出会强制结束线程
sleep(4);
printf("主线程退出\n");
return 0;
}



三. 补充:LWP 与原生线程库的封装关系
很多同学会疑惑:用户态的 pthread 线程,和内核态的 LWP 是什么关系?TCB 又是什么?这里先进行一个简单的讲解,我们后面的博客中还会详细说的
-
内核层:轻量级进程 LWP
- Linux 内核没有线程的概念,只认
task_struct,我们创建的每一个用户态线程,内核都会对应创建一个 LWP(轻量级进程),拥有独立的task_struct,是 CPU 调度的最小单位。
- Linux 内核没有线程的概念,只认
-
用户层:pthread
-
线程库线程库是运行在进程地址空间共享区的动态库,负责用户态线程的管理:
- 为每个线程分配独立栈空间、创建 TCB(线程控制块);
- 维护线程 ID、线程属性、退出码等信息;
- 底层通过
clone系统调用,让内核创建 LWP,建立用户态线程和内核 LWP 的 1:1 对应关系。
-
用户态线程 ID vs 内核 LWP 号
pthread_t:用户态线程 ID,本质是进程地址空间中 TCB 结构体的地址,仅在进程内唯一;- LWP 号:内核态轻量级进程 ID,系统全局唯一,通过
ps -aL可查看,是 CPU 调度的唯一标识。

结尾:
html
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
思维导图

结语:本篇我们完整梳理了进程与线程的资源划分,以及线程创建、终止、等待、分离的全生命周期控制,这是 Linux 多线程开发的核心基础。理解了资源共享与私有,就能避开多线程开发的大部分坑;掌握了线程控制接口,就能完成基础的多线程程序开发。下一篇我们会继续深入线程安全、同步互斥、互斥锁、条件变量等进阶内容,欢迎点赞收藏,一起交流学习~
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
