多线程(二)线程安全

本文详细介绍了线程互斥的概念,包括临界资源、临界区、线程互斥的必要性,以及互斥量的原理和操作。接着讨论了线程安全和可重入的概念,分析了线程不安全和线程安全的情况。此外,还探讨了死锁的概念和避免策略,以及线程同步的重要性,如条件变量的使用规范。
摘要由CSDN通过智能技术生成

目录

一丶线程互斥

1丶互斥概念

1丶 临界资源与临界区

2丶线程互斥的必要性

 互斥与原子性的概念

非互斥的example;

2丶互斥量

3.互斥量操作

1.互斥量初始化

2.互斥量销毁

3.互斥量加锁

 4.互斥量解锁

5.互斥量使用

※.互斥量的原理

1.加锁是如何保证原子性的

2.如何保证锁是原子性的

 二丶可重入与线程安全

1.概念

2.常见的线程不安全的情况

3.常见的线程安全情况

4.常见的不可重入情况

5.常见的可重入情况

6.可重入与线程安全关系

7.可重入与线程安全区别

 三丶常见锁概念

1.死锁

2. 死锁的四个必要条件

3.避免死锁 

四丶线程同步

1.同步概念与竞态条件

 2.条件变量

3.条件变量设置

1.初始化

2.销毁

3.等待条件满足

4.唤醒等待

4.使用条件变量完成同步

5.为什么pthread_cond_wait需要互斥量 

 6.条件变量使用规范


一丶线程互斥

1丶互斥概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
  • 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

1丶 临界资源与临界区

两个进程共享的资源就是临界资源,定义的全局变量,缓冲区,等等。临界资源的代码就是临界区。线程是共享进程所有资源的~

2丶线程互斥的必要性

int num=0;
void*run(void*args)
{
    while(1)
    {
        num++;
        cout<<"l am new thread"<<" "<<num<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,run,nullptr);
    while(1)
    {
        cout<<"i am main thread"<<" "<<num<<endl;
        sleep(1);
    }
    pthread_join(tid,nullptr);
    return 0;
}

从这个例子我们就可以发现两个线程共享同一份资源。但是这样的两个线程肆无忌惮的去访问同一个资源会不会有问题? 

 互斥与原子性的概念

互斥: 

       你在打野怪突然对面来人了,也想打这个野怪这时候怎么办?你们两个一起打然后打完的钱一人一半么?显然是不可能的。这里你和敌人就被看作是两个线程,野怪也就是临界资源。而解决这一问题的方法就是要么你把他干死或者他把你干死,你们两个争斗的行为就是互斥,当只剩一个人的时候就可以安全的打这只野怪了。

        互斥的作用:保证在任何时候有且只有一个执行流进入临界区对临界资源进行访问。

原子性:

        能保证这段命令是一次执行的(成功或者失败都可以)称之为有原子性,一个人想给你转一些钱,你收到这笔钱你俩操作同时成功,要么就是同时失败。

        一个程序要被保证有原子性,就要被完整的执行,要么就是不执行,执行时候被打断那么这个程序就是非原子性的~

         原子性:子性指的是不可被分割的操作,该操作不会被任何调度机制打断,该操作只有两态,要么完成,要么未完成。

非互斥的example;

我们实现一个抢票机制,多人一起抢票最后票被强光 ~

        


using namespace std;
 
int tickets = 1000;//定义一个全局变量,这就是临界资源,1000张票  
  
void* ThreadRotinue(void* args)  
{  
    int id = *(int*)args;  
    delete (int*)args;
  
    while(true)
    {  
        if(tickets > 0)
        {  
            usleep(10000); //usleep函数能把线程挂起一段时间, 单位是微秒(千分之一毫秒)。 
            printf("我是[%d] 我抢的票是:%d\n", id, tickets);  
            tickets--;  //抢票,票数递减
        }  
        else
        {  
            break;  
        }  
    }                                                                                                                                                                 
}                                                                                                                                                 
                                                                                                                                                  
int main()                                                                                                                                        
{                                                                                                                                                 
    pthread_t tid[5];
                                                                                                                             
    for(int i = 0; i < 5; i++)//主线程创建出5个线程去抢票
    {                                                                                                                   
        int* id = new int(i);                                                                                            
        pthread_create(tid + i, nullptr, ThreadRotinue, id);                                                                                        
    }
                                                                                                                                             
    for(int i = 0; i < 5; i++)
    {                                                                                                                   
        pthread_join(tid[i], nullptr); //等待线程                                                                                                            
    }
                                                                                                                                             
    return 0;                                                                                                                                     
}

很明显这个抢票系统出了问题!为什么会出现这样的问题?

  1. if 语句判断条件为真以后,代码可以并发的切换到其他线程
  2. usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段
  3. --tickets 操作本身就不是一个原子操作

ticket--其实并不是原子性的操作~

我们在vs上面进行调试打开反汇编

 可以发现ticket--执行了三次反汇编~

  1. mov :将共享变量tickets从内存加载到寄存器中
  2. sub: 更新寄存器里面的值,执行-1操作
  3. mov:将新值,从寄存器写回共享变量ticket的内存地址

         首先T1把ticket值读进寄存器,--后就被切走了,此时寄存器中保存着T1的上下文信息此时T1保存的值就是999,然后被挂起等待。

        然后T2就被调度了,T2把值放进去看到是999也执行--操作,然后T1又被切过来,但是这时T1中的上下文数据是999,他又要和寄存器的值做交换,--。这样已经出问题了~

        这里可以解释ticket--也并不是原子性操作~

2丶互斥量

  • 大部分情况,线程使用的
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值