Linux--多线程

线程概念

  • 并行:多个线程在不同的cpu上同时运行
  • 并发:一个cpu会被不同的线程轮流使用
  • 进程ID:tgid (线程组ID)
    • 主线程ID:PID = tgid
    • 工作线程ID : PID 与 tgid不同
    • 线程是操作系统调度的最小单位
    • 进程是操作系统分配资源的最小单位
  • 创建线程的开销小
    • 线程的切换成本低
    • 线程占用的资源小
    • 多线程可以并发执行
    • top -H -p pid : 查看线程的cpu占用
    • gcore pid:强制生成coredump文件
    • tread apply all bt : 查看所有线程的调用堆栈
  • 线程独有和共享
    • 共享
      • 虚拟地址空间;代码段数据段;文件描述符表;用户ID和组ID;当前进程的工作路径
    • 独有
      • tid线程号;;信号屏蔽字;一组寄存器;errno
  • 多线程的优缺点
    • 效率高
    • 一个线程异常可能会导致整个进程的崩溃,健壮性
    • 缺乏访问控制

线程控制

线程创建
  • int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* start_routine, void* arg)
    • thread : 出参, 线程空间的首地址
    • attr : 结构体, 对线程属性设置(线程栈的大小,起始位置,线程的分离属性,线程的优先级调度), NULL采用默认属性
    • start_routine : 函数指针, 线程的入口函数, 参数void*,返回值void* (指定线程从哪一个函数开始执行)
    • arg : 给入口函数传的参数, 要注意生命周期
    • gcc t.c -o t -g -lpthread 链接线程库
线程终止
  • 1.从入口函数return
  • 2.pthread_exit(void* retval)
    • retval 线程的退出信息, 可以传NULL
    • 主线程调用pthread_exit, 进程不会退出, 变成ZL+状态, 工作线程不会收到影响
  • 3.pthread_cancel(pthread_t thread)
    • pthread_t pthread_self(); 返回调用线程的线程标识符
线程等待
  • 线程有一个默认属性 joinable, 当线程退出的时候不会主动释放资源,需要其他线程线程的资源, 否则会造成内存泄漏. 进程虚拟地址空间共享区属于线程的独有资源
  • pthread_join(pthread, void**)
  • 三种情况
    • 1.入口函数return
      • void** 保存返回值的地址
    • 2.pthread_exit(void *)
      • void** 保存参数的地址
    • 3.pthread_cancel(pthread)
      • void** 保存常数 PTHREAD_CANCEL
线程分离
  • 线程终止的时候不需要由其他线程释放资源, 由操作系统回收
  • int pthread_detach(pthread_t pthread);
  • pthread_t pthread_self(); 返回调用线程的线程标识符

线程安全

互斥锁
  • 互斥锁
    • 在底层是互斥量, 计数器, 取值只有 0 和 1
    • 计数器本身也是一个变量, 不是直接进行++ – 操作, 而是使用内存交换
  • 使用
    • 定义: pthread_mutex_t
    • 初始化 : int pthread_mutex_t(*mutex, *attr); 用宏初始化
    • 加锁
      • pthread_mutex_lock(* mutex);阻塞
      • pthread_trylock(* mutex);非阻塞, 拿不到锁资源,返回 EBUSY
      • pthread_timedlock(* mutex, * abs_timeout);超时后报错返回 ETIMEOUT
        • abs_timeout : 结构体指针, 结构体中有两个参数, 一个是秒, 二是纳秒
    • 解锁
      • pthread_mutex_unlock(* mutex);
    • 销毁
      • pthread_mutex_destroy(* mutex);
  • 死锁的四个必要条件
    • 互斥 : 一个资源在同一时间只能由一个执行流占用
    • 请求与保持: 拥有一个资源, 还要请求其他资源
    • 循环等待 :
    • 不可剥夺 : 所获得资源只能自己主动释放
  • 避免死锁
    • 破坏必要条件
    • 加锁顺序一致
    • 确保锁资源被释放
    • 一次性分配资源
条件变量
  • pthread_cond_t
  • 等待
    • pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*)
      • 将线程放到PCB等待队列中(为啥在放到PCB等待队列之后解锁?)
      • 解锁
      • 被唤醒, 竞争锁资源
  • 唤醒
    • pthread_cond_signal(pthread_cond_t *)
      • 唤醒等待队列至少一个执行流
    • pthread_cond_broadcast()
      • 唤醒所有执行流
  • 销毁
    • pthread_cond_destroy()
  • 使用注意事项:
    • while判断资源是否可用
    • 需要两个条件变量, 两个PCB等待队列
信号量
  • 完成线程/进程间的同步和互斥
  • 比条件变量多了一个资源计数器,不需要程序员判断资源是否可用
  • 初始化
    • sem_init(sem_t* sem, int pshared, int value)
      • pshared : 该信号量用于进程间还是线程间
        • 0 线程间
        • 1 进程间, 在内核申请一块共享内存,使得所有进程都能访问到
      • value : 资源的数量
  • 等待
    • sem_wait(sem_t*) : 阻塞等待
    • sem_trywait(sem_t*) : 非阻塞等待
    • sem_timedwait(sem_t*, const struct timespce*)
  • 唤醒
    • sem_post(sem_t*)
      • 归还资源, 资源计数器加1
  • 销毁
    • sem_destroy(sem_t*)
读写锁
  • 少量写 大量读的场景
  • 读模式加锁/写模式加锁/未加锁状态
  • 同一时间只能一个执行流占有写锁, 多个执行流占有读锁
  • pthread_rwlock_t
  • pthread_rwlock_rdlock
  • pthread_rwlock_wrlock
  • pthread_rwlock_unlock
  • pthread_rwlock_destroy

生产者消费者模型

  • 优点
    • 可以解耦合
    • 支持并发
    • 支持忙闲不均
  • 1 2 3
    • 一个队列 两个角色 三种关系

线程池

  • 一个线程安全的队列 + 一堆线程
  • 所有的线程使用的同一个入口函数
  • 队列中的的节点为->数据+处理函数
  • 封装任务类->封装线程池

设计模式

  • 对于常见问题的解决方案
  • 创建性模式: 构建对象
    • 单例模式
  • 结构性模式: 不同的对象形成大型对象结构
    • 适配器模式
  • 行为性模式:
    观察者模式: 一对多
单例模式
  • 提供唯一的类的实例
  • 场景:
    • 数据池
    • 内存池
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值