多个线程对临界资源的访问存在安全冲突问题,要保证访问操作的安全性;
实现方式(同步与互斥):
同步与互斥
互斥:同一时间只有一个线程可以访问资源;
同步:通过一些条件让资源访问更加合理;
互斥:*
互斥锁:(悲观锁)
乐观锁是CAS锁;
本质上是0/1计数器,通过0/1来对资源的访问状态进行标记,使用前加锁使用后解锁,且所有线程使用一把互斥锁(也是一种临界资源,互斥锁本身的操作必须安全);
读写锁
读共享,写互斥
互斥锁操作流程:
①定义互斥锁;
pthread_mutex_t mutex;
//可以在全局定义一个锁然后所有线程都可以用
②初始化互斥锁;
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t* attr);
//后面那个参数是属性,通常设置为NULL;
③访问资源之前进行加锁;
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);
死锁*
死锁产生条件:*
- 互斥条件:一个资源一次只能被一个进程使用;
- 不可剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放;
- 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系;
死锁预防:破坏死锁产生条件*
①线程之间保证加锁解锁顺序一致,避免形成加锁环路;
②使用非阻塞加锁,加了A锁之后申请B锁,如果B锁申请不到则释放A锁;
死锁避免:银行家算法*
将系统运行定义了两种状态:安全&非安全;
分为三个表:所有资源表,已分配资源表,当前请求资源表;
同步:
实现方式:条件变量&信号量
条件变量:提供了pcb等待队列,提供了使线程阻塞以及唤醒阻塞的接口,且需要搭配互斥锁使用,条件判断需要用户自己进行;
信号量:自身提供了资源获取条件判断的功能,且不一定需要搭配互斥锁使用;
同步:一个线程要获取资源必须满足资源获取条件,如果不满足则阻塞,获取条件满足后,唤醒阻塞的线程去获取(什么时候阻塞什么时候唤醒由用户设置);
条件变量接口:
①定义条件变量
pthread_cond_t cont;
②初始化条件变量
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *mutex);
③阻塞线程
int pthread_cond_wait(pthread_cond_t *cond, pthread_cond_t *mutex);
//这个接口包含了三个操作(解锁、休眠、被唤醒后加锁)
//由于有锁所以是原子操作
//且pthread_cond_wait接口内部包含了解锁操作
④唤醒阻塞线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒至少一个线程;
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒所有线程;
//唤醒的顺序不一定
⑤使用完毕则释放
int pthread_cond_destroy(pthread_cond_t *cond);