• 【多线程】无锁编程以及CAS


    无锁编程 / lock-free / 非阻塞同步

    无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。
    实现非阻塞同步的方案称为“无锁编程算法”( Non-blocking algorithm)。
    lock-free是目前最常见的无锁编程的实现级别(一共三种级别)。

    为什么要 Non-blocking sync ?

    使用lock实现线程同步有很多缺点:
    * 产生竞争时,线程被阻塞等待,无法做到线程实时响应。
    * dead lock。
    * live lock。
    * 优先级翻转。
    * 使用不当,造成性能下降。
     
    如果在不使用 lock 的情况下,实现变量同步,那就会避免很多问题。虽然目前来看,无锁编程并不能替代 lock。

    实现级别

    非同步阻塞的实现可以分成三个级别:wait-free/lock-free/obstruction-free。
     
    wait-free
    是最理想的模式,整个操作保证每个线程在有限步骤下完成。
    保证系统级吞吐(system-wide throughput)以及无线程饥饿。
    截止2011年,没有多少具体的实现。即使实现了,也需要依赖于具体CPU。
     
    lock-free
    允许个别线程饥饿,但保证系统级吞吐。
    确保至少有一个线程能够继续执行。
    wait-free的算法必定也是lock-free的。
     
    obstruction-free
    在任何时间点,一个线程被隔离为一个事务进行执行(其他线程suspended),并且在有限步骤内完成。在执行过程中,一旦发现数据被修改(采用时间戳、版本号),则回滚。
    也叫做乐观锁,即乐观并发控制(OOC)。事务的过程是:1读取,并写时间戳;2准备写入,版本校验;3校验通过则写入,校验不通过,则回滚。
    lock-free必定是obstruction-free的。

    CAS原语

    LL/SC, atom read-modify-write
    如果CPU提供了Load-Link/Store-Conditional(LL/SC)这对指令,则就可以轻松实现变量的CPU级别无锁同步。
    LL [addr],dst:从内存[addr]处读取值到dst。
    SC value,[addr]:对于当前线程,自从上次的LL动作后内存值没有改变,就更新成新值。
    上述过程就是实现lock-free的 read-modify-write 的原子操作。
     
    CAS (Compare-And-Swap)
    LL/SC这对CPU指令没有实现,那么就需要寻找其他算法,比如CAS。
    CAS是一组原语指令,用来实现多线程下的变量同步。
    在 x86 下的指令CMPXCHG实现了CAS,前置LOCK既可以达到原子性操作。截止2013,大部分多核处理器均支持CAS。
    CAS原语有三个参数,内存地址,期望值,新值。如果内存地址的值==期望值,表示该值未修改,此时可以修改成新值。否则表示修改失败,返回false,由用户决定后续操作。
    复制代码
    Bool CAS(T* addr, T expected, T newValue) 
     { 
          if( *addr == expected ) 
         { 
              *addr =  newValue; 
               return true; 
         } 
         else 
               return false; 
     }
    复制代码
    ABA 问题
    thread1意图对val=1进行操作变成2,cas(*val,1,2)。
    thread1先读取val=1;thread1被抢占(preempted),让thread2运行。
    thread2 修改val=3,又修改回1。
    thread1继续执行,发现期望值与“原值”(其实被修改过了)相同,完成CAS操作。
     
    使用CAS会造成ABA问题,特别是在使用指针操作一些并发数据结构时。
     
    解决方案
    ABAʹ:添加额外的标记用来指示是否被修改。

    语言实现

    Java demo
    AtomicInteger atom = new AtomicInteger(1);
    boolean r = atom.compareAndSet(1, 2);
     
    C# demo
    int i=1;
    Interlocked.Increment(ref i);
     
     
    补充:ConcurrentLinkedQueue(循环CAS+volatile 实现的wait-free并发算法)
  • 相关阅读:
    js调试工具Console命令详解
    tomcat如何正确的开启远程调试功能
    Cleanup failed to process the following paths错误的解决
    MySQL查看SQL语句执行效率
    MYSQL 的静态表和动态表的区别, MYISAM 和 INNODB 的区别
    JAX-RS之queryparam、PathParam、DefaultValue、FormParam、Context、RestController等
    C语言中的printf函数的输入输出问题
    leetcode 21.Merge Two Sorted Lists ,java
    初识C++ 中的STL
    leetcode 58.Length of Last Word
  • 原文地址:https://www.cnblogs.com/itplay/p/11121691.html
Copyright © 2020-2023  润新知