并发环境下的一些同步方法

一、锁

1. 互斥锁(mutex_lock)

  最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒。此时有线程切换的消耗。

  ① 原子性:互斥锁加锁操作是一个原子操作,这保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

  ② 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

  ③ 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

2. 自旋锁(spin_lock)

  与互斥锁相同,除了加锁失败后的后续操作不同。自旋锁加锁失败后并不是睡眠等待唤醒,而是循环检测保持者是否已经释放了锁,不会让出cpu,会一直忙碌。没有线程切换的消耗。

3. 两种锁的适用情况

  当锁内执行的操作耗时较少,即等待的消耗远远小于线程切换的消耗,选择自旋锁。

  当持有锁的时间比较长时,则选择互斥锁。长时间的自旋操作会给cpu带来非常大的执行开销。

二、原子操作

  原子操作的狭义是单条cpu指令实现的操作,广义是指一段不会被线程调度机制打断的操作。

1. C语言用汇编实现+1的原子操作

复制代码
int inc(int *value, int add)
{
    int old;
    __asm__ volatile(
    “lock; xaddl %2, %1;”    //xaddl是把%2和%1相加并放到%1里
    : “=a” (old)
    : “m” (*value), “a”(add)
    : “cc”, “memory”
        );
    return old;
}
    
复制代码

 

三、CAS

1. CAS的概念

  CAS是Compare And Swap/Compare And Set。CAS是根据乐观锁概念实现的一种无锁同步机制。

  线程在读取数据是不进行加锁,在准备修改数据时,先去查询原值,操作的时候比较原值是否被修改,若未被其他线程修改则写入数据,最后返回交互操作是否成功。

  常用CAS自旋锁,适用与并发较少的情况。

2. CAS自旋锁的缺点

①ABA问题

  ABA问题指线程1获取变量时值为A,此时资源被抢占,线程1休眠,变量值在此期间完成A->B->A的变换操作,线程1被唤醒后判断A与期待值相同,认为A没有被改变过,继续执行交换操作。

  具体案例:

  •     我们假设一个提款机的例子。假设有一个遵循CAS原理的提款机,小灰有100元存款,要用这个提款机来提款50元。
  •     由于提款机硬件出了点问题,小灰的提款操作被同时提交了两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。
  •     理想情况下,应该一个线程更新成功,一个线程更新失败,小灰的存款值被扣一次。
  •     线程1首先执行成功,把余额从100改成50.线程2因为某种原因阻塞。这时,小灰的妈妈刚好给小灰汇款50元。
  •     线程2仍然是阻塞状态,线程3执行成功,把余额从50改成了100。
  •     线程2恢复运行,由于阻塞之前获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以会成功把变量值100更新成50。
  •     原本线程2应当提交失败,小灰的正确余额应该保持100元,结果由于ABA问题提交成功了。

  解决方法:增加版本号信息,当变量发生变化时,版本号更新,在进行Compare操作时,不仅要比较旧值与期待值,同时也要比较版本号有没有发生改变。

②循环case

  CAS自旋锁长时间自旋会消耗大量cpu时间。

  解决方法:在低并发的情况下使用;破环自旋的for死循环,增加自旋次数判断,超过次数退出自旋

③只能更新一个值

  CAS中只能同时更新一个值

  解决方法:加锁;将多个需要改变的值封装成一个对象

posted @   幻cat  阅读(55)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示