多线程编程
可重入函数
- 概念:如果一个函数只访问它本身局部的变量和函数 则成为可重入函数
volatile 关键字
- 作用:保证某变量的内存可见性 避免被编译器做内存级的优化
线程
概念
线程是一个执行分支 执行粒度比进程更细 调度成本更低 线程是进程内部的一个执行流 线程是CPU调度的基本单位
TCB
线程控制块,属于PCB,Linux中用PCB模拟线程的TCB
接口
头文件 #include<pthread.h>(原生线程库)
- int pyhread_create(pthread_t* thread,const pthread_attr_t attr, void(start_routine)(void ),void* arg );
- 参数
thread 输出型参数 表明创建的线程ID
attr 线程属性 默认为nullptr
start_routine 函数指针 被视为新线程的功能
arg 传入线程的参数 最终会传到函数指针中 - 返回值 成功返回0 失败返回错误码
- 参数
- int pthread_join(pthread_t thread, void**retval)
- 参数 thread 对应线程ID retval 输出参数 可以拿到新线程退出的结果
- 功能 等待并回收对应线程
- 返回值 成功返回0 失败错误码
- void pthread_exit(void* retval)
- 参数 获取返回的信息
- 功能 终止调用该函数的线程
- int pthread_cancel(pthread_t thread)
- 参数 发送想取消线程的ID
- 功能 取消一个线程 且该线程的退出码为-1
- 返回值 成功为0 失败错误码
- pthread_t pthread_self(void)
- 功能 返回该调用该函数的线程ID
C++中同样有线程库 头文件#include
其将线程包装成了类 但底层仍然调用了原生线程库 接口如下
thread t1(thread_run);
t1.detch() t1.join()…
线程退出
- 线程函数执行完毕
- exit 只要有一个线程调用了exit 其所属进程就会退出 则全部线程都会退出
- pthread_exit()
线程内部存储
__thread int key;
将全局变量变为线程的局部变量 (g++/gcc 编译器的编译条件 仅可限制内置变量)
线程分离
- 当不关心返回值 join是一种负担的话 这个时候可以告诉系统 当线程结束的时候 自动释放线程资源
- 接口 int pthread_detach(pthread_t thread);
功能 :使该线程无法join 当线程结束时自动释放线程资源
相关命令
- ps -aL 查找线程
线程互斥—锁
mutex
全局锁可以直接用宏PTHREAD_MUTEX_INITIALIZER 初始化
接口
头文件#include<pthread.h>
- int pthread_mutex_init(pthread_mutex_t * restcict mutex,const pthread_mutexattr_t * restrict attr);
- 参数:
mutex 互斥锁
attr 属性 一般设为nullptr - 功能:初始化锁
- 参数:
- int pthread_mutex_destroy(pthread_mutex_t* mutex);
- 功能:销毁锁
- int pthread_mutex_lock(pthread_mutex_t* mutex);
- 功能:申请锁 即加锁 如果申请不到则被阻塞住
- int pthread_mutex_unlock(pthread_mutex_t*mutex);
- 功能:解锁
死锁
概念
死锁是指在一组进程中的各个进程均占有不会释放的资源,但因相互申请被其他进程锁占用不会释放的资源而处于一种永久等待状态
死锁四个必要条件
- 互斥条件:存在一个资源每次只能被一个执行流使用
- 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:一个执行流已获得的资源在未使用完之前不能强行剥夺
- 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
如何避免
- 如果可以不加锁 那就不加锁
- 主动释放锁 比如多次申请不到就不申请锁了
接口 int pthread_mutex_trylock(pthread_mutex_t* mutex); 如果申请不到锁 直接出错返回 - 按照顺序申请锁
- 统一控制线程释放锁
线程同步—条件变量
接口
头文件#include<pthread.h>
- int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr);
- 参数 cond 条件变量指针 attr 属性 一般设置为nullptr
- 功能 初始化一个条件变量
- 返回值 成功返回0 失败返回错误码
另一种初始化方式 pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
- int pthread_cond_destroy(pthread_cond_t* cond)
- 参数 cond 要销毁的条件变量
- 功能 销毁一个条件变量
- 返回值 成功返回0 失败返回错误码
- int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);
- int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex);
- int pthread_cond_broadcast(pthread_cond_t*cond);
- 功能 唤醒全部线程
- pthread_cond_signal(pthread_cond_t* cond);
- 功能 唤醒下一个线程
生产者消费者模型
两种角色
生产者消费者
一个交易场所
通常是缓冲器
三种关系
生产者与生产者 互斥
消费者于消费者 互斥
生产者与消费者 同步与互斥
信号量
概念
每一个线程 在访问对应资源的时候 先申请信号量 申请成功 表示该线程允许使用该资源 申请不成功 目前无法使用该资源
信号量的工作机制:类似于看电影买票 是一种资源的预定机制
二元信号量
当临界资源中的资源数目只有一个的时候
接口
#include<semaphore.h>
- int sem_init(sem_t* sem, int pshared, unsigned int value);
- 参数
sem 信号变量指针
pshared 0:线程间共享 非0进程间共享
value 信号量初始值 - 功能 初始化信号量
- 参数
- int sem_destroy(sem_t* sem);
- 功能 销毁信号量
读者写者问题
一个交易场所
两种身份
读者
写者
三种关系
读者与读者 无关
写者与写者 互斥
读者与写者 互斥同步
接口
- pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
- pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
- pthread_rwlock_unlock(pthread_unlock_t*
- rwlock);