线程概念
- 并行:多个线程在不同的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
- 1.入口函数return
线程分离
- 线程终止的时候不需要由其他线程释放资源, 由操作系统回收
- 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_wait(pthread_cond_t*, pthread_mutex_t*)
- 唤醒
- pthread_cond_signal(pthread_cond_t *)
- 唤醒等待队列至少一个执行流
- pthread_cond_broadcast()
- 唤醒所有执行流
- pthread_cond_signal(pthread_cond_t *)
- 销毁
- pthread_cond_destroy()
- 使用注意事项:
- while判断资源是否可用
- 需要两个条件变量, 两个PCB等待队列
信号量
- 完成线程/进程间的同步和互斥
- 比条件变量多了一个资源计数器,不需要程序员判断资源是否可用
- 初始化
- sem_init(sem_t* sem, int pshared, int value)
- pshared : 该信号量用于进程间还是线程间
- 0 线程间
- 1 进程间, 在内核申请一块共享内存,使得所有进程都能访问到
- value : 资源的数量
- pshared : 该信号量用于进程间还是线程间
- sem_init(sem_t* sem, int pshared, int value)
- 等待
- sem_wait(sem_t*) : 阻塞等待
- sem_trywait(sem_t*) : 非阻塞等待
- sem_timedwait(sem_t*, const struct timespce*)
- 唤醒
- sem_post(sem_t*)
- 归还资源, 资源计数器加1
- sem_post(sem_t*)
- 销毁
- sem_destroy(sem_t*)
读写锁
- 少量写 大量读的场景
- 读模式加锁/写模式加锁/未加锁状态
- 同一时间只能一个执行流占有写锁, 多个执行流占有读锁
- pthread_rwlock_t
- pthread_rwlock_rdlock
- pthread_rwlock_wrlock
- pthread_rwlock_unlock
- pthread_rwlock_destroy
生产者消费者模型
- 优点
- 可以解耦合
- 支持并发
- 支持忙闲不均
- 1 2 3
- 一个队列 两个角色 三种关系
线程池
- 一个线程安全的队列 + 一堆线程
- 所有的线程使用的同一个入口函数
- 队列中的的节点为->数据+处理函数
- 封装任务类->封装线程池
设计模式
- 对于常见问题的解决方案
- 创建性模式: 构建对象
- 单例模式
- 结构性模式: 不同的对象形成大型对象结构
- 适配器模式
- 行为性模式:
观察者模式: 一对多
单例模式
- 提供唯一的类的实例
- 场景:
- 数据池
- 内存池