Linux 线程(POSIX)核心教程

一、线程核心概论

1.1 基本定义

Linux 中,线程被称为 "轻量级进程(LWP,Light Weight Process)",线程属于进程------ 一个进程可以包含多个线程,所有线程共享进程的资源(如内存空间、文件描述符、信号处理等),同时拥有独立的执行流。

1.2 核心作用

与进程一致,线程的核心价值是实现并发执行,尤其适合处理:

  • 耗时任务(如 IO 操作、网络通信);
  • 并行计算(如多核心数据处理);
  • 程序模块化拆分(如一个线程处理输入,一个线程处理输出)。

1.3 关键特征

维度 核心描述
资源分配 进程是系统最小的资源分配单位 (如内存、CPU 时间片);线程是系统最小的执行单位
层级关系 进程内的所有线程是平级关系,无父子之分;进程默认包含一个 "主线程"(main 函数对应的线程)
资源范围 线程共享进程的全局资源(堆、全局变量、文件描述符),但拥有独立的栈区(默认 8MB)

二、线程与进程的核心区别

特性 线程 进程
资源共享 共享进程资源(堆、全局变量等),仅栈区独立 资源完全独立(虚拟地址空间隔离)
稳定性 一个线程崩溃会导致整个进程崩溃 单个进程崩溃不影响其他进程
创建开销 仅需开辟独立栈区(8MB),开销极低 需创建完整虚拟地址空间(3GB),开销大
并发度 高(切换成本低) 低(切换需切换地址空间)
通信方式 直接读写共享变量(需同步) 需 IPC(管道、消息队列、共享内存等)

三、线程编程核心步骤(POSIX 标准)

Linux 线程编程遵循 POSIX 标准(pthread 库),核心流程为:创建多线程线程空间执行任务线程资源回收(线程退出后栈区默认不释放,需手动 / 自动回收)

3.1 线程相关命令(调试 / 监控)

1. 查看线程信息

bash

运行

复制代码
# 查看线程的PID、PPID、LWP(轻量级进程ID)、状态、命令
ps -eLo pid,ppid,lwp,stat,comm

# 更详细的线程信息(含线程ID、CPU占用等)
ps -eLf
2. 辅助命令(路径控制)

线程 / 进程的工作路径控制依赖以下函数(常与线程任务结合):

c

运行

复制代码
// 获取当前工作路径
char *getcwd(char *buf, size_t size);
// 参数:buf-存储路径的数组;size-数组最大长度
// 返回值:成功返回buf指针,失败返回NULL

// 切换工作路径
int chdir(const char *path);
// 参数:path-目标路径(绝对/相对)
// 返回值:成功0,失败-1

3.2 线程核心函数(POSIX)

1. 创建线程:pthread_create

c

运行

复制代码
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
  • 功能 :创建一个新线程,线程启动后执行start_routine函数;
  • 参数
    • thread:输出参数,存储新线程的 ID(需提前定义);
    • attr:线程属性(如栈大小、分离属性),默认传NULL(使用默认属性);
    • start_routine:线程的执行函数(回调函数),格式为void* 函数名(void*)
    • arg:传给回调函数的参数(void * 类型,可传递任意数据);
  • 返回值:成功返回 0,失败返回错误码(非 0)。
2. 获取当前线程 ID:pthread_self

c

运行

复制代码
pthread_t pthread_self(void);
  • 功能:获取当前线程的 ID;
  • 返回值 :当前线程 ID(类型为unsigned long int,打印用%lu)。
3. 线程退出:pthread_exit

c

运行

复制代码
void pthread_exit(void *retval);
  • 功能:线程主动退出(替代 return,更适合线程场景);
  • 参数retval:线程退出的返回值("临死遗言"),可被pthread_join获取;
  • 注意 :主线程调用pthread_exit仅退出自身,不影响其他子线程。
4. 取消线程:pthread_cancel

c

运行

复制代码
int pthread_cancel(pthread_t thread);
  • 功能:主动请求终止指定线程;
  • 参数thread:要取消的线程 ID;
  • 返回值:成功 0,失败非 0;
  • 注意:线程需处于 "可取消状态" 才会响应(默认可取消)。
5. 阻塞回收线程资源:pthread_join

c

运行

复制代码
int pthread_join(pthread_t thread, void **retval);
  • 功能:阻塞等待指定线程退出,并回收其资源(栈区);
  • 参数
    • thread:要回收的线程 ID;
    • retval:输出参数,存储线程的退出返回值(对应pthread_exit的参数);
  • 返回值:成功 0,失败非 0;
  • 核心特点:若线程未退出,调用者会阻塞,直到线程结束。
6. 设置线程分离属性:pthread_detach

c

运行

复制代码
int pthread_detach(pthread_t thread);
  • 功能:设置线程为 "分离属性",线程退出后资源(栈区)由系统自动回收;
  • 参数thread:要设置的线程 ID(可传pthread_self()设置自身);
  • 返回值:成功 0,失败非 0;
  • 核心价值 :无需调用pthread_join,避免僵尸线程(线程退出后资源未释放);
  • 注意 :设置分离属性后,无法再调用pthread_join回收该线程。

四、线程编程实战示例

4.1 基础示例:创建线程 + 阻塞回收

c

运行

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 线程执行函数
void* thread_func(void* arg) {
    char *msg = (char*)arg;
    printf("子线程ID:%lu,参数:%s\n", pthread_self(), msg);
    sleep(2);
    // 线程退出,返回值为"thread exit"
    pthread_exit((void*)"thread exit");
}

int main() {
    pthread_t tid;
    int ret;
    void *retval;

    // 创建线程
    ret = pthread_create(&tid, NULL, thread_func, (void*)"hello thread");
    if (ret != 0) {
        perror("pthread_create failed");
        exit(1);
    }

    printf("主线程ID:%lu,创建子线程ID:%lu\n", pthread_self(), tid);

    // 阻塞回收线程,获取返回值
    ret = pthread_join(tid, &retval);
    if (ret != 0) {
        perror("pthread_join failed");
        exit(1);
    }

    printf("子线程退出,返回值:%s\n", (char*)retval);
    return 0;
}

4.2 编译 & 运行

bash

运行

复制代码
# 编译(必须链接pthread库)
gcc thread_demo.c -o thread_demo -lpthread

# 运行
./thread_demo

4.3 输出结果

plaintext

复制代码
主线程ID:140709267896000,创建子线程ID:140709259507456
子线程ID:140709259507456,参数:hello thread
子线程退出,返回值:thread exit

4.4 分离属性示例

c

运行

复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    // 设置自身为分离属性
    pthread_detach(pthread_self());
    printf("子线程ID:%lu,执行任务\n", pthread_self());
    sleep(2);
    printf("子线程退出,资源自动回收\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);

    // 主线程等待3秒,确保子线程执行完成
    sleep(3);
    printf("主线程退出\n");
    return 0;
}

五、线程编程核心注意事项

  1. 编译必须链接 pthread 库 :pthread 是独立库,编译时需加-lpthread,否则报 "undefined reference to pthread_create";
  2. 资源共享与同步:线程共享进程资源,多线程操作共享变量时需加互斥锁(pthread_mutex),避免数据竞争;
  3. 线程退出方式
    • 子线程可通过pthread_exit主动退出;
    • 主线程用pthread_exit仅退出自身,用exit会终止整个进程;
  4. 资源回收
    • 非分离线程必须调用pthread_join回收,否则会产生 "僵尸线程";
    • 分离线程无需回收,系统自动释放资源;
  5. 线程安全 :避免在多线程中使用非线程安全函数(如strtok),优先使用线程安全版本(如strtok_r)。
相关推荐
王璐WL5 小时前
【数据结构】栈和队列及相关算法题
数据结构·算法
再遇当年5 小时前
因为研究平台arm,RK3588交叉编译误把我笔记本X86平台的/x86_64-linux-gnu文件删除,导致联想拯救者笔记本中的ubuntu系统损坏
linux·arm开发·ros·gnu·交叉编译·x86
Zhi.C.Yue5 小时前
React 的桶算法详解
前端·算法·react.js
小热茶5 小时前
浮点数计算专题【五、 IEEE 754 浮点乘法算法详解---基于RISCV的FP32乘法指令在五级流水线的运行分析与SystemC实现】
人工智能·嵌入式硬件·算法·systemc
Giser探索家5 小时前
卫星遥感数据核心参数解析:空间分辨率与时间分辨率
大数据·图像处理·人工智能·深度学习·算法·计算机视觉
q_30238195565 小时前
破局路侧感知困境:毫米波雷达+相机融合算法如何重塑智能交通
数码相机·算法
Robert--cao5 小时前
人机交互(如 VR 手柄追踪、光标移动、手势识别)的滤波算法
人工智能·算法·人机交互·vr·滤波器
云青山水林5 小时前
算法竞赛从入门到跳楼(ACM-XCPC、蓝桥杯软件赛等)
c++·算法·蓝桥杯
李斯维5 小时前
第14 章 使用 shell:初始化文件
linux·bash·unix