【Linux】线程安全--互斥锁和死锁

目录:
  • 互斥锁的原理
  • 互斥锁的接口
  • 死锁的产生与预防
  • 互斥锁的代码体现 --抢车票的例子

线程安全

概念:多个执行流对同一临界资源进行争抢访问,但是不会造成数据二义
实现
同步: 通过条件判断实现对临界资源访问的时序合理
互斥:同一时间只能一个执行流(线程)能够访问临界资源,实现数据操作安全

我们这片讲述的就是 互斥的实现- 互斥锁。

互斥锁就是实现临界资源只被一个线程访问。

互斥锁原理:

本质:互斥锁本质就是一个计数器,只有两种状态标记的计数器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);

死锁:

概念:多个线程对锁资源争抢访问,但是因为推进顺序不当,导致相互等待,造成程序无法继续,这就是死锁。

死锁产生的四个必要条件

  1. 互斥条件 — 同一时间锁只能有一个线程能够获取
  2. 不可剥夺条件 — 线程A加的锁只有线程A能解
  3. 请求与保持条件 — 线程A拿着A锁去请求B锁,若请求不到,则线程A一直保持A的锁不释放
  4. 环路等待条件(如图)
    在这里插入图片描述
    线程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;
    }    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值