线程 --- 嵌入式(Linux)

1、线程的基本概念

线程是进程内的最小执行单元,也被称为轻量级进程(LWP)。一个进程可以包含多个线程,所有线程共享进程的核心资源,同时拥有自己独立的执行上下文(如程序计数器、寄存器、栈)。

2、线程和进程的区别

维度 进程 线程
资源分配 操作系统资源分配的最小单元(独立的地址空间、文件描述符、内存等) 操作系统CPU任务调度的最小单元(不独立分配资源,共享所属进程的资源)
地址空间 每个进程有独立的虚拟地址空间 无独立地址空间,共享进程的文本区、数据区、堆区、文件描述符、信号处理等;仅独享栈区、寄存器上下文、程序计数器 (PC)、线程 ID
独立性 完全独立,一个进程崩溃不影响其他进程 高度依赖进程,一个线程崩溃会导致整个进程终止(进程内所有线程退出)
通信 / 同步 需借助 IPC(管道、消息队列、共享内存等),成本高 直接读写共享变量即可通信,需通过锁 / 信号量等机制解决同步问题
切换开销 大(需切换地址空间、页表等) 小(仅切换执行上下文,无需修改地址空间)

3、线程的创建

  • 调用 pthread_create 创建线程时,系统会为该线程分配独立的栈空间 (Linux 默认栈大小为 8MB,可通过 ulimit -s 查看 / 修改);
  • 线程的文本区(代码)、数据区(全局 / 静态变量)、堆区(动态分配内存)、文件描述符等,均复用所属进程的资源;
  • 线程创建成功后,新线程与主线程并发执行(无固定执行顺序,由 CPU 调度决定)。

4、线程的调度

  • 线程调度规则基本等同于进程调度(支持抢占式调度、时间片轮转、优先级调度等);
  • 宏观上:多个线程 "同时" 执行(并行),因为 CPU 多核或快速切换;
  • 微观上:单核 CPU 中,同一时刻只有一个线程在执行(串行),依赖 CPU 时间片切换实现 "伪并行"。

5、线程的消亡

  • 等同于进程的消亡,线程消亡需要回收线程空间
  • 线程消亡的触发条件:
    1. 线程函数执行完毕(隐式退出);
    2. 调用 pthread_exit 主动退出;
    3. 进程终止(所有线程随之消亡);
    4. pthread_cancel 取消(被动退出)。
  • 线程消亡后必须回收其资源(线程空间):
    • 若未回收,线程会变成 "僵尸线程",占用系统资源(类似僵尸进程);
    • 回收方式:pthread_join(阻塞回收)、pthread_detach(分离线程,系统自动回收)。

6、多线程和多进程优缺点

特性 多进程 多线程
安全性(稳定性) 高:地址空间独立,一个进程崩溃不影响其他进程 低:共享进程资源,一个线程崩溃导致整个进程终止;需处理资源竞争 / 线程安全问题
执行效率 低(切换 / 通信开销大)因为切换进程任务时需要映射不同的物理地址空间,增大系统开销 高(切换 / 通信开销小)因为在同一进程空间内部切换不同的任务
编程复杂度 低(无需处理资源竞争) 高(需处理同步 / 互斥问题)
资源占用 高(每个进程独立占资源) 低(共享进程资源)
适用场景 独立任务、对稳定性要求高(如后台服务、多程序运行) 高并发、数据共享频繁(如网络服务、GUI 程序)
资源竞争 无:地址空间独立,无共享资源竞争 有:共享数据区 / 堆区,需通过互斥锁、条件变量等防止 "竞态条件"
通信难度 高:需 IPC(管道、共享内存等)多进程通信不方便,因为空间独立,没有共享空间 低:直接访问共享变量(全局 / 堆),仅需同步机制(锁 / 信号量)多线程通信非常方便,多线程数据区、堆区共享

7、线程相关函数接口

7.1 pthread_create

  • 函数原型
cpp 复制代码
#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
  • 功能:创建一个新线程,线程启动后执行start_routine函数;
  • 参数:
    • thread:输出参数,存放新线程的 ID(注意:可用 %#x pthread_self()获取id );
    • attr:线程属性(如栈大小、分离状态),默认属性传NULL
    • start_routine:线程函数入口(函数指针),必须是 void* (*)(void*) 类型(返回值和参数均为 void*);
    • arg:传给线程函数的参数(若传递栈变量地址,需确保变量生命周期长于线程(避免野指针);
  • 返回值:成功返回 0,失败返回错误码 (不是 errno,需用 strerror() 解析,如 perror 不适用);
  • 编译注意:必须链接 pthread 库,编译命令加-lpthread(如gcc test.c -o test -lpthread)。

7.2 pthread_exit

  • 函数原型
cpp 复制代码
void pthread_exit(void *retval);
  • 功能:主动终止当前线程,且不会影响同进程的其他线程;
  • 参数:retval:线程退出的返回值(可被pthread_join()获取),若无需返回值传NULL
  • 注意:主线程调用pthread_exit()仅终止自身,子线程仍会继续执行;若主线程直接return,则整个进程终止。
  • 返回值:缺省

7.3 pthread_join

  • 函数原型
cpp 复制代码
int pthread_join(pthread_t thread, void **retval);
  • 功能:阻塞等待指定线程终止,并回收其资源(避免僵尸线程);
  • 参数:
    • thread:要回收的线程 ID;
    • retval:输出参数,存放线程的返回值(即pthread_exit()retval),无需获取则传NULL
  • 返回值:成功返回 0,失败返回错误码(如线程已分离、线程不存在);
  • 核心作用:
    1. 阻塞回收线程空间资源;
    2. 实现线程同步(主线程等待子线程执行完毕)。
    3. 注意:一个线程只能被一个 pthread_join 回收,重复调用会失败。

示例(创建 + 回收线程):

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

void *thread_func(void *arg) {
    char *msg = (char *)arg;
    printf("子线程ID:%lu,收到参数:%s\n", (unsigned long)pthread_self(), msg);
    
    // 堆内存,手动分配,主线程回收
    int *ret_val = (int *)malloc(sizeof(int));
    *ret_val = 100;
    pthread_exit((void *)ret_val); // 返回堆地址
}

int main() {
    pthread_t tid;
    char *msg = "Hello Thread";
    int err;

    err = pthread_create(&tid, NULL, thread_func, (void *)msg);
    if (err != 0) {
        printf("创建线程失败:%s\n", strerror(err));
        exit(1);
    }
    printf("主线程:创建的子线程ID为%lu\n", (unsigned long)tid);

    void *ret;
    err = pthread_join(tid, &ret);
    if (err != 0) {
        printf("回收线程失败:%s\n", strerror(err));
        exit(1);
    }
    // 访问堆数据
    printf("主线程:子线程退出,返回值为%d\n", *(int *)ret);
    free(ret); // 必须手动释放堆内存,避免内存泄漏

    return 0;
}
相关推荐
茫忙然13 分钟前
U 盘搭建免驱 Linux 便携系统教程
linux·服务器
knighthood200131 分钟前
vscode插件开发的一些过程
ide·vscode·编辑器
一起逃去看海吧1 小时前
dify-03
java·linux·开发语言
fengyehongWorld1 小时前
Linux 根据端口进行的相关查询
linux
lihao lihao1 小时前
linux匿名管道
linux·运维·服务器
うちは止水2 小时前
weston出图调试
linux·wayland·weston
STDD2 小时前
Farming Simulator 25(模拟农场 25) Linux 专服搭建完全指南
linux·运维·javascript
好好风格2 小时前
宝塔面板 HTTPS 端口证书不生效排查记录
linux·运维·nginx
用户2367829801683 小时前
Linux pgrep 命令详解:按名称查找进程 PID 的高效方法
linux