Linux——多线程(二):线程安全,线程不安全现象,线程互斥-互斥锁(原理,接口,使用),死锁


1.线程安全

线程安全:多个执行流,访问临界资源,不会导致程序产生二义性

  • 执行流:理解为线程
  • 访问:指的是对临界资源进行操作
  • 临界资源:指的是多个线程都可以访问到的资源
    eg:全局变量,某个结构体(不能是定义在某个线程入口函数内),某个类的实例化指针
  • 临界区:代码操作临界资源的代码区域称之为临界区
  • 二义性:结果会有多个

2. 如何描述线程不安全的现象

2.1 正常变量操作的原理

正常情况,假设我们定义一个变量 i 这个变量 i 一定是保存在内存当中的,当我们要对这个变量 i 进行计算的时候,是CPU(两大核心功能:算术运算和逻辑运算)来计算的,假设要对变量 i = 10 进行 +1 操作,首先要将内存中的 i 的值为 10 告知给寄存器,此时,寄存器中就有一个值 10,然后让CPU对寄存器中的这个 10 进行 +1 操作,CPU +1 操作完毕后,将结果 11 回写到寄存器当中,此时寄存器中的值被改为 11,然后将寄存器中的值回写到内存当中,此时 i 的值为 11

图解如下:
在这里插入图片描述

2.2 线程不安全现象的描述

此处线程不安全现象的描述是基于 2.1 正常变量操作的原理描述的,分以下几步描述:
(1)假设场景,有几个线程,每个线程想做什么事

假设有两个线程,线程A和线程B,线程A和线程B都想对全局变量 i 进行++

(2)分线程去描述,体现出:线程切换,上下文信息,程序计数器

假设全局变量 i 的值为 10,线程A从内存中把全局变量 i = 10 读到寄存器当中,此时,线程A的时间片到了,线程A被切换出来了,线程A的上下文信息中保存的是寄存器中的i = 10,程序计数器中保存的是下一条即将要执行的 ++ 指令,若此时线程B获取了CPU资源,也想对全局变量 i 进行 ++ 操作,因为此时线程A并未将运算结果返回到内存当中,所以线程B从内存当中读到的全局变量 i 的值还是10,然后将 i 的值读到寄存器中,然后再在CPU中进行 ++ 操作,然后将 ++ 后的结果 11,回写到寄存器,寄存器再回写到内存,此时内存当中 i 的值已经被线程B进行 ++ 后改为了 11,然后线程B将CPU资源让出来,此时线程A再切换回来的时候,它要执行的下一条指令是程序计数器中保存的对 i 进行 ++ 操作 ,而线程A此时 ++ 的 i 的值是从上下文信息中获取的,上下文信息中此时的 i = 10 ,此时线程A在CPU中完成对 i 的 ++ 操作,然后将结果 11 回写给寄存器,然后由寄存器再回写给内存,此时内存中的 i 被线程B改为了 11,虽然 ,线程A和线程B都对全局变量 i 进行了 ++ ,按理说最终全局变量 i 的值应该为12,而此时全局变量 i 的值却为11

(3)总结

线程A对全局变量 i 加了一次,线程B也对全局变量 i 加了一次,而此时,全局变量的值为 11 而不是 12,由此就产生了多个线程同时操作临界资源的时候有可能产生二义性问题(线程不安全现象)

图解如下:
在这里插入图片描述

2.3 代码模拟线程不安全现象(模拟黄牛抢票)

假设我们600张票,让两个黄牛抢,即定义一个全局变量为600,创建两个线程,并在两个线程的线程入口函数中对这个全局变量进行修改(大于等于0减一),最终,我们有可能会看到两个线程拿到了同一张票,代码如下所示:

    1 #include<stdio.h>                                                                                                                     
    2 #include<unistd.h>
    3 #include<pthread.h>
    4 
    5 int get_ticket = 600;
    6 
    7 void* Mythreadstart(void* arg)
    8 {
   
    9     while(1)
   10     {
   
   11         //当票大于0,抢到一张票,总数减一
   12         if(get_ticket > 0)
   13         {
   
   14             printf("i have %d,i am %p\n",get_ticket,pthread_self());
   15             get_ticket--;
   16         }
   17         //票数小于等于0,结束
   18         else
   19         {
   
   20             pthread_exit(NULL);
   21         }
   22     }
   23     return NULL;
   24 }
   25 
   26 int main()
   27 {
   
   28     pthread_t tid[2];
   29     //创建两个工作线程        
   30     for(int i = 0;i < 2;++i)
   31     {
          
   32         int ret = pthread_create(&tid[i],NULL,Mythreadstart,NULL);
   33         if(ret < 0)                      
   34         {
                    
   35             perror("pthread_create");
   36             return 0;                                               
   37         }                
   38     }    
   39                              
   40             
   41     //线程等待
   42     for(int i = 0;i < 2;++i)   
   43     {
       
   44         pthread_join(tid[i],NULL);
   45     }           
   46  
   47     printf("pthread_join is end...\n"
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值