Linux
线程共享了进程的资源(地址空间, 页表等), 多个线程同时访问同一个资源就可能产生问题:数据竞争
- 临界资源: 多个线程共享的资源
- 临界区: 访问临界资源的区域
- 互斥: 任何时刻, 只有一个执行流能进入临界区
- 同步: 以一定顺序访问临界资源
- 原子性: 要么完成, 玩么未完成
锁
- 死锁:一组进程占有资源,且不释放 + 互相申请, 导致永久等待的状态
- 互斥锁:访问机制, 保证任何时刻, 只有一个线程能访问临界资源
- 自旋锁:访问机制, 申请锁失败会一直检测锁的状态
- 读写锁:读可并发, 写会独占资源. 一般适合读取数据次数 > 写数据的次数
- 悲观锁:读取数据先加锁, 其它线程访问数据时会被阻塞
- 乐观锁:读取数据时不加锁,更新数据时会比较数据是否被修改(被修改:重试或放弃等)
死锁
产生条件
- 互斥条件:一个资源每次只能被一个执行流使用
- 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
- 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
避免死锁
- 破坏死锁的四个必要条件
- 加锁顺序一致
- 避免锁未释放的场景
- 资源一次性分配
互斥锁
操作:
- 头文件:<pthread.h>
- int pthread_mutex_init(pthread_mutex_t *mutex)函数进行初始化
- 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)销毁
原理:
- lock:1.将0放入寄存器%al中 2. 把寄存器%al的值与内存中mutex的值做交换 3.如果al寄存器的内容>0 , 说明申请锁成功, 就返回0, 否则申请失败, 就挂起等待
- unlock: 把1给内存中的mutex
条件变量
- 概念: 是用于线程通信和同步的机制, 通常与锁一起使用
- 功能: 阻塞等待某种条件, 条件满足后继续执行(A线程等待B线程完成某种任务后,才向后执行)
- 与锁一起使用: A线程在不满足条件时会释放锁, 直到条件就绪会重新获取锁然后继续执行
操作
头文件: <pthread.h>
pthread_cond_init
函数原型:int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
功能:初始化条件变量
参数:cond 是指向条件变量对象的指针,attr 是一个指向线程属性对象的指针,可以为 NULL。
返回值:调用成功返回0,失败返回错误码
pthread_cond_destroy
函数原型:int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数:cond 是指向已初始化的条件变量对象的指针。
返回值:调用成功返回0,失败返回错误码。
pthread_cond_wait
函数原型:int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
功能:等待条件变量,并在收到信号或广播时解除阻塞
参数:cond 是指向条件变量对象的指针,mutex 是与条件变量相关联的互斥锁。
返回值:调用成功返回0,失败返回错误码。
规范: 在while()中等待-->保证条件就绪的时候再被唤醒
pthread_cond_signal
功能:当条件满足时用来唤醒等待在条件变量上的一个线程。
函数原型:int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒一个等待在条件变量上的线程
参数:cond 是指向条件变量对象的指针。
返回值:调用成功返回0,失败返回错误码
pthread_cond_broadcast
函数原型:int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有等待在条件变量上的线程
参数:cond 是指向条件变量对象的指针。
返回值:调用成功返回0,失败返回错误码。
信号量
- 概念: 一种软件资源(本质时计数器 ,对临界资源的预定机制)
- 意义: 不用进入临界区就能知到资源情况(减少临界区内部的判断)
操作
头文件: <semaphore.h> <pthread.h>
sem_init
函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化一个未命名的信号量
参数:
sem 是指向信号量对象的指针,
pshared 用于指示信号量是在进程间共享还是线程间共享,
value 是信号量的初始值。
返回值:调用成功返回0,失败返回-1
sem_destory
函数原型:int sem_destroy(sem_t *sem);
功能:销毁一个未命名的信号量
参数:sem 是指向已初始化的信号量对象的指针
返回值:调用成功返回0,失败返回-1
sem_wait(申请信号量)
函数原型:int sem_wait(sem_t *sem);
功能:等待信号量,如果信号量的值大于0,将其减1;否则将线程阻塞,直到信号量的值大于0
参数:sem 是指向信号量对象的指针
返回值:调用成功返回0,失败返回-1
sem_post(释放信号量)
函数原型:int sem_post(sem_t *sem);
功能:释放信号量,将信号量的值加1,唤醒等待该信号量的线程
参数:sem 是指向信号量对象的指针
返回值:调用成功返回0,失败返回-1