【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:引用计数本身是原子的,线程安全

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

相关推荐
航Hang*2 小时前
第3章:Linux系统安全管理——第1节:Linux 防火墙部署(firewalld)
linux·服务器·网络·学习·系统安全·vmware
云飞云共享云桌面2 小时前
SolidWorks三维设计不用单独买电脑,1台服务器10个设计用
运维·服务器·数据库·3d·电脑
acaad2 小时前
访问信创系统的服务器报错Received fatal alert: handshake_failure
运维·服务器
大树882 小时前
【无标题】
大数据·运维·服务器·人工智能
南境十里·墨染春水2 小时前
linux学习进展 基础命令 vi基础命令
linux·运维·服务器·笔记·学习
Deitymoon2 小时前
linux——读写锁
linux
赵民勇2 小时前
locales包详解
linux
江畔何人初2 小时前
GTID的作用
linux·运维·服务器·mysql·云原生·kubernetes
橘子编程2 小时前
编译原理:从理论到实战全解析
java·linux·python·ubuntu