线程安全(互斥锁、条件变量、信号量、读写锁)——Linux系统编程

本文详细探讨了线程安全问题及其解决方案,包括互斥锁、条件变量、信号量和读写锁的原理及使用。通过实例解释了线程不安全的原因,阐述了解决线程不安全的互斥原则和同步需求。同时,介绍了各种同步机制的使用,如互斥锁的加锁、解锁和死锁避免,条件变量的等待与唤醒,信号量的资源计数器以及读写锁的并发读写特性。
摘要由CSDN通过智能技术生成

线程安全

多个线程同时运行,访问临界资源,不会导致程序结果产生二义性,这样的情况是线程安全的。

临界资源:多线程执行流共享的资源叫临界资源。

临界区:每个线程内部访问临界资源的代码

访问:在临界区当中对临界资源进行非原子操作

原子操作:不会为任何调度机制打断的操作,该操作只有两种状态,要么完成,要么未完成。

线程不安全的原理

举个栗子:我们规定一个场景

  1. 假设现在在同一个程序当中有两个线程,线程A和线程B,并且有一个int类型全局变量,值为10;线程A和线程B在各自的入口函数当中对这样一个变量进行++操作。
  2. 线程A拥有CPU之后,对全局变量进行++操作,并非是原子操作,也就是意味着线程A,在执行++过程中有可能会被打断。假设线程A刚将全局变量的数值10读到CPU的寄存器当中来,就被切换出去了(程序计数器保存下一条执行的指令,上下文信息当中保存寄存器的值,这两个是用来当线程A再次拥有CPU的时候恢复现场使用的)
  3. 这时有可能线程B拥有了CPU资源,对全局变量进行++操作,并将10加成11放回到了内存当中
  4. 线程A再次拥有CPU之后,根据程序计数器和上下文信息恢复现场,继续往下执行,从寄存器当中读到的值仍然是10,++操作之后还是11

线程A和线程B各自对全局变量进行了++操作,理论上全局变量应为12,但现在程序计算的结果有是11,这样的线程就是不安全的,访问临界资源对程序的结果造成了二义性。

线程不安全怎么解决

互斥:任何时刻,互斥保证有且只有一个执行流进入临界区访问临界资源。

同步:为了在互斥的基础上追求资源的合理分配,在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源。

互斥锁

使用互斥锁来保证互斥属性,互斥锁的底层是互质量,互斥量本质是一个计数器,该计数器只有两个取值0或者1,当线程获取互斥锁的时候,如果计数器当中的值为0,表示当前线程获取不到互斥锁,反之则表示当前线程可以获取到互斥锁。

原理

互斥锁如何保证操作的原子性?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQ9yGOIk-1613641108316)(image/互斥锁.png)]

  1. 加锁的时候
    1. 寄存器当中的值直接赋值为1
    2. 将寄存器当中的值和计数器当中的值交换(互换指令xchgb)
    3. 判断当前寄存器中的值,得出加锁结果
      • 当寄存器的值为1时,表示可以加锁。
      • 当寄存器的值为0时,表示不可以加锁。
  2. 解锁的时候
    1. 将寄存器的值赋为1
    2. 交换寄存器和计数器当中的值手机果然很奇妙。

使用

定义互斥锁

pthread_mutex_t;//互斥锁变量类型

初始化互斥锁

int pthread_mutex_init(pthread_mutex_t
*restrict mutex,const pthread_mutexattr_t *restrict attr);
  • ​ mutex:传入互斥锁变量的地址
  • ​ attr:属性,一般传递NULL,采用默认属性
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
  • pthread_mutex_t本身是一个结构体类型,PTHREAD_MUTEX_INITIALIZER宏定义是一个结构体的值

  • #define PTHREAD_MUTEX_INITIALIZER {
         {0,0,0,0,0,_PTHREAD_SPINS,{0,0}}};
    

加锁

  1. int <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值