
🔥个人主页:Cx330🌸
❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》
《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔
🌟心向往之行必能至
🎥Cx330🌸的简介:

目录
[二、 进程 VS 线程:资源共享与私有划分](#二、 进程 VS 线程:资源共享与私有划分)
[1.1 线程间共享的进程资源](#1.1 线程间共享的进程资源)
[1.2 线程私有的独立资源](#1.2 线程私有的独立资源)
[1.3 关键问题:为什么一个线程崩溃,整个进程都会退出?](#1.3 关键问题:为什么一个线程崩溃,整个进程都会退出?)
[3.1 pthread 线程库](#3.1 pthread 线程库)
[3.2 线程创建:pthread_create](#3.2 线程创建:pthread_create)
[3.3 多线程创建 -- 构建任务](#3.3 多线程创建 – 构建任务)
[3.4 线程终止:3 种合法终止方式](#3.4 线程终止:3 种合法终止方式)
[3.4.1 方式 1:线程入口函数 return 返回](#3.4.1 方式 1:线程入口函数 return 返回)
[3.4.2 方式 2:pthread_exit:线程主动终止自身](#3.4.2 方式 2:pthread_exit:线程主动终止自身)
[3.4.3 方式 3:pthread_cancel:终止同进程的其他线程](#3.4.3 方式 3:pthread_cancel:终止同进程的其他线程)
[3.5 线程等待:pthread_join](#3.5 线程等待:pthread_join)
[3.6 多线程的优化写法------线程等待](#3.6 多线程的优化写法——线程等待)
[3.7 线程分离:pthread_detach](#3.7 线程分离:pthread_detach)
前言:
在多线程开发中,入门级的线程创建与启动仅仅是起点。当业务场景变得复杂,并发量提升、资源竞争加剧时,如何合理划分资源、精准控制线程行为,直接决定了程序的性能、稳定性与可维护性。本文聚焦线程进阶的两大核心模块------资源划分 与线程控制,结合底层原理与实战场景,帮你跳出"只会用线程,不会管线程"的困境,真正实现多线程的高效运用。
一、前置认知:为什么需要资源划分与线程控制?
操作系统中,进程是资源分配的基本单位,而线程是CPU调度的基本单位,线程依赖进程存在并共享其大部分资源。在未进行合理资源划分与线程控制的场景中,极易出现两大问题:
-
资源竞争混乱:多个线程争抢同一资源(如内存、文件描述符),引发竞态条件、死锁等问题,导致程序运行异常或性能瓶颈;
-
线程行为失控:线程创建/销毁频繁、执行顺序不可控、空闲线程占用资源,不仅会增加CPU上下文切换开销,还可能导致系统资源耗尽,甚至JVM崩溃。
简单来说,资源划分是"合理分配蛋糕",让每个线程都能高效获取所需资源;线程控制是"规范吃蛋糕的秩序",让线程按预期执行、协作,避免混乱。二者相辅相成,是多线程进阶的核心必修课。
二、 进程 VS 线程:资源共享与私有划分
线程的 "轻量化 ",本质是 共享了进程的绝大部分资源,仅私有运行必需的最小上下文。我们先把共享与私有资源划清,这是理解线程所有特性的基础。
1.1 线程间共享的进程资源
同进程内的所有线程,共享进程地址空间内的绝大部分资源,无需额外申请,这也是线程创建 / 切换成本极低的核心原因:
- 地址空间核心段:代码段(Text Segment)、数据段(Data Segment)、堆区、共享区
- 全局变量、静态变量、堆内申请的内存,所有线程都可直接访问修改
- 文件系统相关:文件描述符表、当前工作目录、用户 id / 组 id
- 一个线程打开的文件,其他线程可直接读写
- 信号相关:每种信号的处理方式(SIG_IGN/SIG_DFL/ 自定义处理函数)
- 信号是发给进程的,任意线程收到信号,都会触发整个进程的信号处理函数
- 进程内核数据:页表、mm_struct 内存描述符、vm_area_struct 内存区域结构
1.2 线程私有的独立资源
线程要独立被 CPU 调度执行,必须私有运行时的上下文数据,这些资源线程间完全隔离,互不影响:
- 线程 ID(TID):用户态 pthread 库的唯一标识,内核态对应 LWP 号
- 寄存器上下文:CPU 寄存器的值,线程切换时保存 / 恢复,保证执行流不混乱
- 独立栈空间:每个线程有自己的私有栈,存放局部变量、函数调用栈帧,互不干扰
- errno 变量:系统调用错误码,线程私有,避免多线程间错误码互相覆盖
- 信号屏蔽字:每个线程可独立设置信号屏蔽规则,不影响其他线程
- 调度优先级:线程可独立设置调度优先级,由内核单独调度
- 线程局部存储(TLS):线程私有的全局变量,仅当前线程可访问
1.3 关键问题:为什么一个线程崩溃,整个进程都会退出?
这是面试高频考点,结合资源划分就能瞬间理解:
- 线程触发异常(除零、野指针、非法内存访问),内核会向进程发送致命信号,而非单独发给线程;
- 信号的处理方式是进程级共享的,进程收到致命信号后,会直接终止整个进程;
- 进程终止后,地址空间、文件描述符等所有资源都会被回收,所有线程自然随之退出。
三、线程控制------精准管控,实现高效协作
Linux 内核仅提供轻量级进程的创建能力,我们日常使用的线程接口,都来自POSIX 标准的 pthread 线程库 ,所有接口都以pthread_ 开头,编译时必须通过**-lpthread**链接线程库
3.1 pthread 线程库
- 头文件:必须包含**<pthread.h>**
- 编译命令:gcc xxx.c -o xxx -lpthread
- 核心特点:线程库运行在用户态,负责线程的管理(TCB 线程控制块、线程栈分配),底层通过
clone系统调用让内核创建 LWP(其实我们之前学习的fork也是这样的)。

代码演示:
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
void *threadrun(void *args)
{
std::string name=static_cast<const char *>(args);
while(true)
{
printf("我是一个新线程:tid: %lu,pid: %d\n",pthread_self(),getpid());
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadrun,(void*)"thread-1");
while(true)
{
printf("创建新线程成功,new tid: %lu,main tid: %lu,pid: %d\n",tid,pthread_self(),getpid());
sleep(1);
}
}

3.2 线程创建:pthread_create
用于创建一个新的用户态线程,是线程控制最基础的接口。
函数原型:
#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)**解析错误信息
配套函数:pthread_self
pthread_t pthread_self(void);
功能:获取当前线程的用户态线程 ID,和进程的getpid()作用一致。
完整实战代码:创建线程
// ./createThread num
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cout << argv[0] << " num" << std::endl;
return 1;
}
int num = std::stoi(argv[1]); // 将字符串转化为数字
std::vector<pthread_t> tids;
for (int i = 0; i < num; i++)
{
// 如果我们要创建多线程呢?
pthread_t tid;
pthread_create(&tid, nullptr, threadrun, (void *)"thread-1");
tids.push_back(tid);
}
sleep(1);
for(auto &tid:tids)
{
printf("创建新线程成功,new tid: 0x%lx,main tid: %lu,pid: %d\n", tid, pthread_self(), getpid());
}
// 主线程
while (true)
{
std::cout<<"main thread running..."<<std::endl;
sleep(1);
}
}

关键说明:
- 线程的执行顺序由内核调度决定,主线程和新线程谁先执行不固定;
- 用户态的pthread_t线程 ID,本质是进程地址空间中线程 TCB 结构体的地址,和内核 LWP 号不是一个概念;
- 主线程如果提前退出(调用 exit/return),整个进程会终止,所有线程都会被强制退出。
3.3 多线程创建 -- 构建任务
Task.hpp
#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;
};
-
#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;}

任务构建版:
// 构建一个任务进行测试
#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;
}

3.4 线程终止:3 种合法终止方式
如果需要只终止某个线程,而不终止整个进程,有 3 种安全合法的方式,严禁在线程内调用 exit (),会导致整个进程退出。
3.4.1 方式 1:线程入口函数 return 返回
最推荐的方式,线程入口函数执行完毕 return,线程自动终止,返回值可被pthread_join获取。
// 测试线程退出
// 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;
}

3.4.2 方式 2:pthread_exit:线程主动终止自身
线程内调用该函数,主动终止自身,效果和 return 一致,返回值可被pthread_join获取。
函数原型
void pthread_exit(void *value_ptr);
- 参数value_ptr:线程退出的返回值,不能指向线程栈内的局部变量;
- 无返回值,调用后线程直接终止,不会再返回。
代码示例:
void *threadrun(void *args)
{
std::string name=static_cast<const char *>(args);
int cnt=5;
while (cnt)
{
sleep(1);
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;
pthread_exit(nullptr);
// exit(3); // 不能用来终止线程,它是用来终止进程的!多线程中,任意一个线程调用exit,都表示整个进程退出
}

3.4.3 方式 3:pthread_cancel:终止同进程的其他线程
一个线程调用该函数,取消同一个进程内的另一个线程,被取消的线程默认退出码是PTHREAD_CANCELED(值为 - 1)。
函数原型
int pthread_cancel(pthread_t thread);
- 参数thread:要终止的目标线程 ID;
- 返回值:成功返回 0,失败返回错误码。
代码示例:
#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;
}
3.5 线程等待:pthread_join
线程退出后,其资源不会被自动释放,必须通过pthread_join等待线程退出,回收资源,否则会造成内存泄漏。
函数原型
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。
完整实战代码:线程等待全场景
void *threadrun(void *args)
{
std::string name=static_cast<const char *>(args);
int cnt=5;
while (cnt)
{
sleep(1);
printf("我是一个新线程:tid: 0x%lx,pid: %d,name: %s,cnt: %d\n",
pthread_self(), getpid(),name.c_str(),cnt);
cnt--;
sleep(1);
// return nullptr;
// pthread_exit(nullptr);
}
return (void*)0;
}
int main()
{
pthread_t tid;
char threadname[gsize];
snprintf(threadname,gsize,"thread-%d",1);
pthread_create(&tid, nullptr, threadrun, (void*)threadname);
void *ret=nullptr;
pthread_join(tid,&ret);
printf("join %lx success,ret code: %lld\n",tid,(long long)ret);
return 0;
}




3.6 多线程的优化写法------线程等待
Task.hpp
#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;
};
// 多线程的优化
#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;
}
3.7 线程分离:pthread_detach
默认情况下,线程是joinable状态,退出后必须通过pthread_join回收资源。如果我们不关心线程的返回值,不想阻塞等待线程退出,可以将线程设置为分离状态,线程退出后,系统会自动回收其资源,无需手动 join。
函数原型
int pthread_detach(pthread_t thread);
- 参数thread:要分离的目标线程 ID;
- 返回值:成功返回 0,失败返回错误码。
使用方式
- 其他线程分离:主线程创建子线程后,调用**pthread_detach(tid)**分离子线程;
- 线程自分离:子线程内调用**pthread_detach(pthread_self())**分离自身
完整实战代码:线程分离
#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;
}
四、总结:线程进阶的核心逻辑
线程进阶的本质,是从"使用线程"到"管理线程"的转变。资源划分的核心是"隔离"------通过业务、资源、任务类型的拆分,减少竞争、提升资源利用率;线程控制的核心是"协作"------通过生命周期管理、顺序控制、中断处理,确保线程按预期执行,避免失控。
实际开发中,没有绝对最优的资源划分和线程控制方案,需结合业务场景、并发量、硬件资源灵活调整。记住:好的多线程设计,不是"线程越多越好",而是"资源分配合理、线程管控精准",既能发挥并发优势,又能保证系统稳定。