day27 同步互斥
同步互斥
线程资源是共享的,此时会出现多个线程对全局变量的访问,导致数据错误,以此引入同步互斥
临界资源:多个线程抢夺的资源(全局变量,文件,数据库,内核资源)
临界区:访问临界资源的代码
互斥:同一时间内只允许一个线程访问临界资源(共享资源)
同步:在互斥的基础上保证访问者的顺序
解决同步互斥问题:互斥锁,信号量,条件变量
互斥锁
概念
对于要访问临界资源的线程做上锁操作,如果上锁成功,则访问成功,如果上锁失败,则阻塞并等待互斥锁解锁再解除阻塞,再执行
速记
函数 | 功能 | 参数 | 返回值 |
pthread_mutex_init | 初始化 | &mutex, NULL | 永0 |
pthread_mutex_lock | 上锁 | &mutex | 成功0,失败非0 |
pthread_mutex_unlock | 解锁 | &mutex | 成功0,失败非0 |
pthread_mutext_destroy | 销毁锁 | &mutex | 成功0,失败非0 |
pthread_mutex_trylock | 尝试上锁 | &mutex | 成功0,失败非0 |
相关函数
pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
功能:初始化进程锁
定义方式:推荐第二个,我懒 ,可定义为全局
1.int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
2.pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
参数:pthread_mutex_t *mutex 指向内存中mutex锁的地址的指针
const pthread_mutexattr_t *mutexattr 互斥锁属性 快速(默认),递归,错误检错锁
默认NULL
返回值:永为0
pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:上锁,如果锁资源被占用,阻塞等待,直到锁资源被释放,解除阻塞上锁成功
参数: pthread_mutex_t *mutex 指向内存中mutex锁的地址的指针
返回值:成功返回0,失败返回非0
pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁,释放锁资源
参数: pthread_mutex_t *mutex 指向内存中mutex锁的地址的指针
返回值:成功返回0,失败返回非0
pthread_mutext_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁锁资源
参数: pthread_mutex_t *mutex 指向内存中mutex锁的地址的指针
返回值:成功返回0,失败返回非0
pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:尝试上锁,锁资源被占用了不会阻塞,报错EBUSY
参数: pthread_mutex_t *mutex 指向内存中mutex锁的地址的指针
返回值:成功返回0,失败返回非0
死锁
1.重复上同一把锁
2.锁资源没有释放,线程异常退出
3.线程重复上多把锁
信号量
对于要访问临界资源的线程都做申请信号的操作
信号量大于0 | 申请成功,信号量-1 释放成功,信号量+1 |
等于0 | 申请失败,处于阻塞态,直到信号量大于0,解除阻塞 |
互斥锁:称为二值信号量(0,1)
解决问题:一个生产者少量消费者
P操作:申请 信号量-1
V操作:释放 信号量+1
速记
#include <semaphore.h>
函数 | 功能 | 参数 | 返回值 |
sem_init | 初始化 | &sem,0,value | 成功返回0,失败返回-1,更新errno |
sem_wait | 申请P | &sem | 同上 |
sem_post | 释放V | &sem | 同上 |
sem_destroy | 销毁 | &sem | 同上 |
sem_getvalue | 获取信号量 | &sem,&sval | 同上 |
Link with -pthread.
相关函数
sem_init
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化的信号量
参数: sem_t *sem:该指针指向的内存中存储信号量
int pshared: 0:表示线程共享 非0:表示进程共享
unsigned int value:用于信号量的初始化,允许多少个线程同时访问临界资源
返回值: 成功返回0,失败返回-1,跟新errno
sem_wait 申请 P -1
int sem_wait(sem_t *sem);
功能:申请信号量 -1 大于0,成功,等于0阻塞,直到大于0解除
参数: sem_t *sem:该指针指向的内存中存储信号量
返回值: 成功返回-,失败返回-1,根新errno
sem_post 释放 V +1
int sem_post(sem_t *sem);
功能:释放信号量 +1
参数: sem_t *sem:该指针指向的内存中存储信号量
返回值: 成功返回-,失败返回-1,根新errno
sem_destroy
int sem_destroy(sem_t *sem);
功能:销毁信号量
参数: sem_t *sem:该指针指向的内存中存储信号量
返回值: 成功返回-,失败返回-1,根新errno
sem_getvalue
int sem_getvalue(sem_t *sem, int *sval);
功能:获取信号量的值
参数: sem_t *sem:该指针指向的内存中存储信号量
int *sval: 该指针指向的内存中存储信号量的值
返回值: 成功返回-,失败返回-1,根新errno
条件变量
对于不访问临界资源的线程休眠,并设置一个唤醒条件(条件变量),如果该线程需要访问临界资源时,需要通过其他线程唤醒该线程
速记
函数 | 功能 | 参数 | 返回值 |
pthread_cond_init | 初始化 | &cond,NULL | 0,非0 |
pthread_cond_wait | 进程塞入等待队列 | &cond,&mutex | 同上 |
pthread_cond_siganl | 唤醒队列第一个 | &cond | 同上 |
pthread_cond_broadcast | 唤醒整个队列 | &cond | 同上 |
pthread_cond_destroy | 销毁 | &cond | 同上 |
相关函数
pthread_cond_init
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能:初始化条件变量
定义方式:推荐第二个,我懒 ,可定义为全局
1.int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
2.pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
参数: pthread_cond_t *cond:该指针指向的内存中存储条件变量
pthread_condattr_t *cond_attr:条件变量属性
NULL:默认属性
返回值:成功0,失败非0
pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
使用流程:
1.上锁pthread_muext_lock()
2.解锁,休眠 pthread_cond_wait()//解锁,休眠 ,上锁(如果被其他线程唤醒后 重新尝试上锁,如果上锁成功,则唤醒成功 如果上锁失败,则唤醒失败)
3.运行
4.解锁
参数: pthread_cond_t *cond:该指针指向的内存中存储条件变量
pthread_mutex_t *mutex:互斥锁
返回值:成功返回0,失败返回非0
-------------------------------------------
条件变量为什么使用互斥锁:
当多核CPU时,同一时间执行多个线程,且同时调用pthread_cond_wait函数,同时把多个线程存到 休眠队列中,此时这个休眠队列就是多个线程需要抢夺的临界资源,解决互斥问题,需要引入互斥锁 哪个县城抢夺到了互斥锁,就把哪个县城存到休眠队列中,存储以后解锁,其他线程在存储袋休眠队列即可
pthread_cond_siganl
int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒休眠队列的第一个线程
参数: pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:成功返回0,失败返回非0
pthread_cond_broadcast
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有休眠队列的线程
参数: pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:成功返回0,失败返回非0
pthread_cond_destroy
int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数: pthread_cond_t *cond:该指针指向的内存中存储条件变量
返回值:成功返回0,失败返回非0