【Linux复习】:多线程

多线程

线程概念

进程概念

把一个可执行程序运行起来就是进程

代码和数据+内核数据结构(PCB)

Linux下线程实现

进程 :资源分配的基本单位
线程 :CPU 调度的基本单位

Linux 没有真正意义上的线程结构体,而是用 轻量级进程(LWP) 实现,复用进程的 PCB 结构。

线程间的独有与共享

共享(同一进程内线程共享)

代码和数据

自定义函数和全局变量

文件描述符表

信号处理方式

工作目录
独有(每个线程私有)

一组寄存器上下文(核心,调度切换用)

独立的栈空间(核心,保证调用不混乱)

线程 ID(TID)

信号屏蔽字(block 位图)

调度优先级

私有 errno

多线程/多进程任务处理的优缺点

优点

创建线程代价远小于创建进程

线程切换开销更低、更快

线程占用资源远少于进程

能更好利用多核 CPU 实现并行

计算密集型任务可拆分到多线程

可同时等待多个不同 IO 操作
缺点

会有额外调度切换开销,轻微性能损失

健壮性低,全局变量容易被意外修改

一个线程异常崩溃,整个进程全部崩溃

编程难度更高,需要处理同步互斥问题

线程控制

线程创建

线程在进程的虚拟地址空间内,拥有独立的栈结构

线程的局部数据、栈空间,存储在进程的 mmap 区域

同一进程内所有线程共享大部分资源,只独有栈、寄存器等少量数据
线程创建函数

c 复制代码
int pthread_create(
    pthread_t *thread,        // 输出型参数:返回创建好的线程ID
    const pthread_attr_t *attr, // 线程属性,默认填 NULL
    void *(*start_routine)(void *), // 线程入口函数(函数指针)
    void *arg                 // 传给线程函数的参数
);

线程终止

线程退出专用函数:

c 复制代码
void pthread_exit(void *retval);

作用:退出当前线程,不影响进程内其他线程

退出数据可通过返回值传递,供pthread_join接收

线程等待

线程退出后,如果不被等待回收,资源会残留在进程地址空间中(类似僵尸进程)

已退出线程的资源不会被新线程自动复用,必须手动等待

等待接口:

c 复制代码
int pthread_join(pthread_t thread, void **retval);

作用:阻塞等待指定线程退出,回收其资源、获取退出信息

线程分离

功能:将线程设置为分离状态,无需主线程pthread_join

线程退出后,操作系统自动回收资源

接口:

c 复制代码
int pthread_detach(pthread_t thread);

适用场景:不关心线程返回值、不需要等待线程退出

注意:分离后的线程不能再被 join,否则报错

线程安全

概念

多个线程对于临界资源的争抢访问操作,但不会造成数据的二义性

如何实现

互斥

保证安全性(同一时间只有一个线程访问)
同步

保证合理性(按顺序执行,避免条件不满足)

互斥实现

互斥锁
信号量

信号量初始化为 1 时就是互斥锁

申请资源时信号量 -1,不足则阻塞

释放资源时信号量 +1,唤醒等待线程

同步实现

条件变量

判断流程和循环判断是否满足条件
信号量

生产消费者模型

full_count:记录缓冲区中已有产品数量

empty_slot:记录缓冲区可用空位数量

生产消费者模型

场景

并发编程的模型,用来解决多线程共享数据的问题
作用

  1. 解耦合
    分离生产者和消费者的任务,使得它们可以独立工作,互不影响
    2. 支持忙闲不均
    有缓冲区,有同步的概念
    3. 支持并发
    生产者和消费者可以同时执行,提高系统的效率,允许存在处理速度的不同
    实现
    线程安全队列
    生产者消费者线程创建
总结:三二一原则

三个关系

生产者与生产者:互斥

消费者与消费者:互斥

生产者与消费者:互斥 + 同步

互斥:不能同时生产和消费

同步:必须先生产,后消费
两种角色

生产者

消费者
一个交易场所

共享内存、队列、缓冲区

读者写者问题模型
读写锁

概念

支持多个读者同时读

只允许一个写者独占写

读共享、写独占
实现原理
读锁获取

  1. 检查写锁是否持有,如果没有持有就允许读锁

  2. 如果写锁持有,或者读锁满了,就阻塞等待

  3. 当满足条件后会唤醒阻塞的线程
    写锁获取

  4. 先检查是否有读锁,再检查是否有写锁,必须独占

  5. 如果不满足,就阻塞等待

  6. 如果满足了,就唤醒阻塞的线程
    读锁释放

如果读完了,就释放读锁,如果没人读了,就唤醒阻塞的写进程
写锁释放

如果写完了,就释放写锁,此时写进程和读进程都可以申请,根据特定策略进行发放
优先级

通常按请求先后顺序分配锁。

自旋锁

概念

线程申请锁失败时,不挂起、不切换上下文,而是原地循环自旋等待,直到锁可用。

实现原理

循环申请某个锁,直到申请成功
使用场景

临界区极短,锁等待时间很短的场景,避免切换开销。

悲观乐观锁

悲观锁

默认认为一定会冲突,每次都先加锁再访问。

互斥锁、读写锁、信号量基本都是悲观锁。
乐观锁

默认冲突概率很低,先修改数据,最后提交时再检测是否冲突。

冲突则回滚重试,适合读多写少、冲突极少的场景。

线程池

概念

预先创建一组可复用的线程 ,当有新任务来就选择一个空闲线程执行,任务结束回到线程池等待下一次分配
解决的问题

解决了线程创建和销毁的开销

方便于对于线程进行管理和控制
实现

管理模块:线程池控制结构(线程数组、任务队列、锁、条件变量等)

工作线程:预先创建好的一批线程,不断从任务队列取任务执行

任务队列:用链表 / 队列保存待执行任务

任务接口:统一的任务函数指针

流程:创建线程池 → 提交任务入队 → 线程竞争取任务 → 执行任务 → 执行完毕回到空闲

线程安全的单例模式

设计模式

单例是全局只存在一个实例。
单例模式概念

一个类只能创建一个对象,资源只加载一次,全局共用。

单例模式实现

饿汉模式

特点:main 函数开始前就已经创建好实例

优点:天然线程安全,无需加锁

缺点:程序启动就初始化,可能浪费资源

c 复制代码
// 饿汉单例
class Singleton {
private:
    static Singleton inst;
    Singleton() {}  // 构造私有
public:
    static Singleton* getInstance() {
        return &inst;
    }
};
Singleton Singleton::inst;

懒汉模式

特点:第一次使用时才创建实例

优点:延时加载,节约启动资源

缺点:线程不安全,必须加锁保证安全

c 复制代码
// 线程安全懒汉单例
class Singleton {
private:
    static Singleton* inst;
    static pthread_mutex_t mtx;
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (inst ==nullptr) {     // 双检查锁
            pthread_mutex_lock(&mtx);
            if (inst== nullptr) {
                inst = new Singleton;
            }
            pthread_mutex_unlock(&mtx);
        }
        return inst;
    }
};
Singleton* Singleton::inst = nullptr;
pthread_mutex_t Singleton::mtx = PTHREAD_MUTEX_INITIALIZER;
STL容器安全

默认线程不安全

多线程同时读写必须自己加锁

智能指针

shared_ptr:引用计数本身是原子的,线程安全

但指向的对象不安全,需要自己保证

相关推荐
YaBingSec4 小时前
玄机网络安全靶场:GeoServer XXE 任意文件读取(CVE-2025-58360)
java·运维·网络·安全·web安全·tomcat·ssh
REDcker4 小时前
Webpack Rollup Vite三者构建对比详解 开发体验与选型考量
运维·webpack·devops
北极熊kw4 小时前
FreeBSD 安装 Xrdp 后,远程桌面时只显示白色终端 Xterm
linux·运维·服务器·rdp·freebsd·xterm
枳实-叶4 小时前
【Linux驱动开发】第二天:内核模块生命周期+内存分配全解
linux·驱动开发
计算机安禾4 小时前
【Linux从入门到精通】第28篇:文本处理三剑客(中)——sed 流编辑器
linux·服务器·编辑器
多租户观察室4 小时前
DNS服务器跟普通服务器有什么区别?
运维·服务器
Will_Ye4 小时前
Ubuntu:系统断网后自动重连指定wifi脚本
linux·运维·ubuntu
学术小白人4 小时前
【见刊通知】ICGEM E2025、IPAT 2025、AISNS 2026、IEAS 2025、BTFM 2026 等数个会议已见刊
运维·服务器·检索·rdlink研发家·见刊
郝学胜-神的一滴4 小时前
深入epoll封装:event_set与event_add核心原理剖析
linux·服务器·开发语言·网络·c++·unix
HABuo4 小时前
【linux(四)】套接字编程--socket套接字及其接口认识
linux·运维·服务器·c语言·c++·ubuntu·centos