Linux多线程学习笔记

本文深入探讨了多线程编程的原因、优点和应用场景,对比了进程、线程和协程的特性。重点介绍了Linux下的线程实现,包括Pthread库的使用、线程创建与终止、线程属性、线程同步机制如互斥锁、条件变量和读写锁。此外,还提到了线程池和超线程技术,以及协程作为轻量级线程的概念及其优势。
摘要由CSDN通过智能技术生成

多线程编程的概念

有了进程,为什么还要多线程?

  • 因为进程开销太大,上下文切换开销大。

多线程编程有哪些优点?

  • 多个线程可以共享地址空间,数据,代码段
  • 因为是共享的所以通信简单方便。

多线程编程主要用在什么地方?

  • 电脑,服务器。
  • SMP、NUMA、MPP
  • 多核、4核8线程

在这里插入图片描述

进程、线程、协程

  • 提高程序运行效率
  • 模块细分,防止程序阻塞
  • 高并发、多核、服务器
  • 线程池、协程

在这里插入图片描述

Pthread线程库

  • 线程的实现: windows、Linux
  • Pthread库: POSIX标准中的thread API
  • Glibc 与 LinuxThread库
  • Glibc 与 NPTL库
  • getconf GNU_LIBPTHREAD_VERSION 查看与C标准库绑定的线程库

使用pthread库

安装man手册

  • apt install glibc-doc manpages-posix-dev

程序的编译

  • gcc main.c -lpthread
  • /usr/lib/libpthread.a

pthread常用API

  • pthread_create
  • pthread_exit
  • pthread_cancel
  • pthread_join
  • pthread_detach
  • pthread_mutex_lock
  • pthread_cond_init
  • pthread_cond_signal
  • pthread_cond_wait
  • pthread_rwlock_rdlock

Linux与Windows的API

在这里插入图片描述

创建一个线程:pthread_create

  • 函数原型:int pthread_create (pthread_t thread, const pthread_attr_tattr, void *(*start_routine)(void *), void *arg);
  • 函数功能:创建一个新线程
  • 参数说明:
  • thread:指向线程ID的指针
  • attr: 线程的属性
  • start_routine:线程执行实体入口
  • arg: 传递给线程的参数
  • typedef unsigned long int pthread_t

线程的终止

终止线程的三种方式

  • 从start_routine正常return
  • 显式调用 pthread_exit
  • 函数原型: void pthread_exit(void *retval);
  • 返回值通过参数retval传递
  • 线程被 pthread_cancel取消

TIPS

  • 线程pthread_exit与exit的区别
  • 线程调用pthread_exit,只会结束当前线程,不影响进程的运行
  • 一个进程中的任何一个线程调用exit,将会结束整个进程

等待线程的终止

线程分两种

Joinable

  • PTHREAD_CREATE_JOINABLE
  • 可通过 pthread_join 等待线程终止
  • 调用pthread_join 的线程会阻塞
  • 一个Joinable线程结束时,资源不会自动释放给系统(堆栈、exit状态等)
  • 当线程终止时, pthread_join 会回收该线程的资源,然后返回
  • 若无pthread_join参与“擦屁股”工作,该线程将变为僵尸线程

Unjoinable

  • PTHREAD_CREATE_DETACHED
  • 可通过 pthread_detach 分离一个线程
  • 当线程终止时,资源会自动释放给系统

API接口

**函数原型:int pthread_join (pthread_t thread, void retval);

  • 函数功能:阻塞掉当前线程,等待指定线程终止
  • 函数终止: void pthread_exit (void *retval);
  • 参数:
    • thread:线程的ID
    • retval:从pthread_exit 返回的值

函数原型: int pthread_detach(pthread_t thread);

  • 函数功能:将指定线程与当前线程分离
  • 参数说明:指定要分离的线程的ID

线程属性

默认属性

  • 调度参数:
  • 线程栈地址:
  • 线程栈大小:8M
  • 栈末尾警戒缓冲区大小:PAGESIZE
  • 线程的分离状态:joinable、detached
  • 继承性:PTHREAD_INHERIT_SCHED、PTHREAD_EXPLICIT_SCHED
  • 作用域:PTHREAD_SCOPE_PROCESS、PTHREAD_SCOPE_SYSTEM
  • 调度策略:SCHED_FIFO、SCHED_RR、SCHED_OTHER

在这里插入图片描述

相关API函数

  • int pthread_attr_init (pthread_attr_t *attr);
  • int pthread_attr_destroy (pthread_attr_t *attr);
  • int pthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize);
  • int pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *stacksize);
  • int pthread_attr_setstackaddr (pthread_attr_t *attr, void *stackaddr);
  • int pthread_attr_getstackaddr (const pthread_attr_t *attr, void **stackaddr);
  • int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);
  • int pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate);

线程调度与运行

线程分类

核心级线程

  • 由内核调度,有利于并发使用多处理器资源

用户级线程

  • 由用户层调度,减少上下文切换开销

线程模型

  • 一对一模型
  • 多对一模型
  • 多对多模型

线程模型

一对一模型

  • 用户线程通过LWP关联内核线程
  • 线程调度由内核完成
  • SMP、并发使用CPU资源
  • 线程间同步由用户层完成
  • Linux、Windows家族(XP之前)

在这里插入图片描述

多对一模型

  • 多个用户线程与一个内核线程关联
  • 线程管理由用户完成、CPU仍以进程为调度单位
  • 单处理器
  • Solaris线程库:Green thread
    在这里插入图片描述

多对多模型

  • 内核线程为CPU调度单元
  • 用户线程管理
    在这里插入图片描述

Linux下的线程

一对一线程模型

  • 一个轻量进程(LWP)对应一个线程,
  • 每个LWP都与一个内核线程关联
  • 内核线程,通过LWP绑定,调度用户线程
  • 内核线程被阻塞,LWP也阻塞,用户线程也阻塞
  • 调度由内核完成
    • SCHED_OTHER:分时调度策略
    • SCHED_FIFO:实时调度策略:FIFO
    • SCHED_RR:实时调度策略:时间片轮转
  • 创建线程、同步等API由用户线程库完成
    • Linuxthreads:线程PID、信号处理存在不足
    • NPTL(Native POSIX Thread Library)
      在这里插入图片描述

LWP与普通用户进程比较

  • LWP只有一个最小的执行上下文和调度程序需要的统计信息
  • 用户进程有独立地址空间,LWP与父进程共享地址空间
  • LWP可以像内核线程一样,全局范围内竞争处理器资源
  • LWP调度可以跟用户进程、内核线程一样调度
  • 每一个用户进程可能有一个或多个LWP
  • 通过clone,各进程共享地址空间和资源
    • CLONE_VM、 CLONE_FS
    • CLONE_FILES、CLONE_SIGHAND
  • $ top -H -p
  • 查看某个指定PID进程下的线程运行
    在这里插入图片描述

线程安全

进程的原生安全

  • 进程的地址空间
    在这里插入图片描述

多线程下的资源访问

资源划分

  • 共享的资源
    • 代码段、数据段、地址空间
    • 打开的文件、信号处理程序
  • 独占的资源
    • 程序计数器:PC
    • 寄存器
    • 栈空间
  • 不同体系不同分配方式
  • 用户线程和管理线程栈是分离的
  • 进程与线程资源
    • 一室一厅一卫
    • 三室一厅一卫

在这里插入图片描述

线程安全

对共享资源的安全访问

  • 临界区与临界资源
  • 关中断
  • 锁、条件变量、读写锁

函数引用

  • 可重入函数
  • 线程安全函数

在这里插入图片描述

在这里插入图片描述

线程同步

多线程下的资源访问

竞争访问

  • 全局变量
  • 缓冲区
  • 三室一厅卫生间
    在这里插入图片描述

互斥锁

相关API函数

  • int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
  • int pthread_mutex_lock(pthread_mutex_t *mutex);
  • int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • int pthread_mutex_destroy(pthread_mutex_t *mutex);

条件变量

互斥锁缺陷:

  • 不断加锁解锁、查询满足条件,开销很大
  • 加锁开销:用户态-内核态-用户态,阻塞在内核态
  • 解锁开销:用户态-内核态-用户态,唤醒等待线程

条件变量

  • 互斥锁(mutex)搭配使用,允许线程阻塞,等待条件满足的信号

优势:

  • 将互斥锁和条件变量绑定
  • 省去了不断加锁解锁的开销
  • 可以使用广播(broadcast)唤醒所有绑定到该条件变量的线程

相关API函数

  • pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  • int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
  • int pthread_cond_signal (pthread_cond_t *cond);
  • int pthread_cond_broadcast (pthread_cond_t *cond);
  • int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
  • int pthread_cond_destroy (pthread_cond_t *cond);

读写锁

  • 互斥锁:同一时刻只允许一个线程读或写
  • 读写锁
    • 允许多个读线程同时读
    • 只允许一个线程写,写的时候会阻塞其它线程(包括读线程)
    • 写优先级高于读
      在这里插入图片描述

相关API函数

  • pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
  • pthread_rwlock_init (pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
  • int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock);
  • int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock);
  • int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock);
  • int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock);
  • int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
  • int pthread_rwlock_destroy (pthread_rwlock_t *rwlock);

线程池

线程的开销

  • 系统调用的开销:线程的创建、销毁
  • 上下文切换、互斥锁等加锁解锁

线程池原理

  • 预先在池中创建一些线程
  • 无任务时,线程阻塞在池中
  • 有任务时,将任务分配到指定的线程执行
  • 池中线程的数目甚至可根据任务多少动态删减

实现原理

管理线程

  • 创建并管理线程
  • 任务分配运行

工作线程

  • 线程池中实际执行任务的线程

任务接口

  • 每个任务的实现接口
    在这里插入图片描述

超线程技术

共享打印机

  • 电脑—打印机
  • 电脑1—打印机
  • 电脑2—打印机

超线程技术

  • Hyper-Threading,简称HT
  • 使用特殊指令将一个物理处理器视为2个逻辑处理器
  • 每个逻辑处理器都可以分配一个线程运行
  • 最大限度地提升CPU资源利用率

CPU微架构资源划分

  • 复制的资源:每个逻辑CPU都有一套完整的体系结构状态
  • 划分的资源:重定序(re-order)缓冲、load/store缓冲、队列等
  • 多任务模式下这些资源划分给2个逻辑CPU使用
  • 单任务模式下这些资源划分给1个逻辑CPU使用
  • 共享的资源:执行计算单元(加、减)、cache、总线

主板的支持

  • 如I7的X58芯片组
  • 主板BIOS支持超线程
  • 操作系统的支持:Windows XP以后、Linux2.4以后
  • 应用层支持:NPTL库

交替工作

  • 共享单元
  • 执行单元
    • 缓存
    • 总线
      应用场所
  • 服务器
  • 工作站

在这里插入图片描述

协程

基本概念

线程的开销

  • 上下文切换开销
  • 互斥锁的开销
    协程
  • 对共享资源的访问由程序本身维护控制
  • 不需要锁,无调度成本,执行效率高
  • 适合彼此熟系的程序组件:合作式多任务、管道
  • 进程 + 线程 -> 进程 + 协程

从进程到线程

在这里插入图片描述

从线程到协程

在这里插入图片描述

协程的实现

  • Python:yield/send、async/await(python3.5以后)
  • Lua:Lua5.0开始使协程
  • Go:支持线程,后端并发
  • C语言:协程库

小结

进程、线程与协程

  • 进程、线程是系统级,而协程是语言级
  • 进程:资源分配的基本单位
  • 线程:程序执行的基本单位
  • 切换成本:进程 > 线程 > 协程
  • 安全性:进程 > 线程 > 协程
  • 协程缺陷:无法利用多核CPU,做到真正的并发
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值