目录:
线程安全
概念:多个执行流对同一临界资源进行争抢访问,但是不会造成数据二义
实现:
同步: 通过条件判断实现对临界资源访问的时序合理
互斥:同一时间只能一个执行流(线程)能够访问临界资源,实现数据操作安全
我们这片讲述的就是 互斥的实现- 互斥锁。
互斥锁就是实现临界资源只被一个线程访问。
互斥锁原理:
本质:互斥锁本质就是一个计数器,只有两种状态标记的计数器0/1
原理:访问临界资源之前,先访问互斥锁,看是否对临界资源能够访问。互斥锁创建出来默认情况下是1,表示可以访问临界资源,访问临界资源之前要把互斥锁的值置为不可访问状态,也就是置为0。
B线程访问临界资源之前也是先访问互斥锁,互斥锁现在标记为1,表示能对临界资源进行访问,线程A就只能等待,等到B线程对资源访问完毕,再将互斥说变量的1改为0,标记为可以访问状态,此时线程A就可以访问临界资源了,访问之前再讲互斥锁的1改为0
但是为了保证临界资源安全,得保证加锁和解锁的安全,但是加锁和解锁要进行0和1的数据改变。一个数字的改变,本身就是有一个过程,需要从内存中取数字加载到寄存器上面进行数据处理,处理完毕之后再放回内存,本身就是一个过程,不安全。
所以我们要想办法将内存到寄存器之间的操作,合成原子操作,就有了下图中的原理:
互斥锁的接口:
注释:pthread_mutex_t 互斥锁变量类型。 它其实是一个结构体,一个锁变量不仅仅是一个计数器,里面还有阻塞队列(要访问资源的pcb等待队列)。
锁的初始化
pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr)
mutex:互斥锁变量
attr:互斥锁属性变量,通常置NULL,不设置它
加锁:(阻塞加锁,加不上则等待别的线程释放资源)
pthread_mutex_lock(pthread_mutex_t* mutex);
尝试加锁(非阻塞加锁,加不了则返回)
pthread_mutex_trylock(pthread_mutex_t * mutex);
限制时长的阻塞加锁
pthread_mutex_timedlock(pthread_mutex_t* mutex);
解锁:
pthread_mutex_unlock(pthread_mutex_t* mutex);
销毁锁:
pthread_mutex_destroy(pthread_mutex_t* mutex);
死锁:
概念:多个线程对锁资源争抢访问,但是因为推进顺序不当,导致相互等待,造成程序无法继续,这就是死锁。
死锁产生的四个必要条件:
- 互斥条件 — 同一时间锁只能有一个线程能够获取
- 不可剥夺条件 — 线程A加的锁只有线程A能解
- 请求与保持条件 — 线程A拿着A锁去请求B锁,若请求不到,则线程A一直保持A的锁不释放
- 环路等待条件(如图)
线程A请求不到B锁,线程B请求不到A锁,所以形成了一个僵局。形成一个环路等待。
死锁的预防:
将线程所用到的锁,先全部申请,一个线程完成之后,释放自己加的所有锁。然后别的线程在加锁进行它的任务。
死锁的避免:
银行家算法:定义三张表,第一张表示当前有哪些锁,第二张表是哪些线程都获取了哪些锁,第三张表示现在谁还要哪些锁。 然后判断,如果我把锁给了一个线程,是否会造成环路等待条件
互斥锁的例子-抢车票
通过四个线程抢车票的例子,认识互斥锁在其中起到的重要作用,不让每个线程同时抢一张车票
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define MAX_THR 4
int tickets = 100;
pthread_mutex_t _mutex; //定义互斥锁变量
void * cattle(void* arg)
{
//pthread_mutex_lock 阻塞加锁
//pthread_mutex_trylock 非阻塞加锁
//pthread_mutex_timedlock 非阻塞限制时长的加锁
while(1)
{
pthread_mutex_lock(&_mutex); //加锁
if(tickets > 0)
{
printf("%p i get a ticket:%d\n",pthread_self(),tickets);
usleep(10000);
tickets--;
pthread_mutex_unlock(&_mutex); //解锁
}
else
{
printf("have no tickets \n");
pthread_mutex_unlock(&_mutex); //有可能车票完了,所以在这里也要解锁
pthread_exit(NULL);
}
}
}
int main()
{
pthread_mutex_init(&_mutex,NULL);
pthread_t tid[MAX_THR];
for(int i = 0; i < MAX_THR; i++)
{
int ret = pthread_create(&tid[i],NULL,cattle,NULL);
if(ret != 0)
{
printf("thread create error\n");
return -1;
}
}
for(int i = 0; i < MAX_THR; i++) //等待线程退出,回收资源
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&_mutex);
return 0;
}