ZooKeeper 本文是读ZooKeeper: Wait-free coordination for Internet-scale systems的笔记,从第一手资料了解zookeeper 概述 -- 提供了一组通用(generic)的,无等待的(wait-free)api,同时提供了两个重要的特性: 保证每个客户端请求FIFO 所有写请求串行进行 结果是读请求能够读本地,从而能够满足可扩展性 --- 的数据对象,从而和其他基于阻塞语义(blocking primitives)有了显著的区别,ZooKeeper在组织wait-free的数据对象借鉴了文件系统的思路,将wait-free的数据对象按层级组织起来 仅仅靠wait-free来实现coordination是不够的,还需要提供操作的有序保证(order guarantees):每个客户端FIFO,所有写请求linearizable。 总结起来,本篇论文的主要内容是: Coordination kernel:基础设施,提出了wait-free的方案 Coordination recipes:应用,个性化primitives的实现 Experience
二、CAS 参考:透过 Linux 内核看无锁编程 非阻塞型同步的三种方案: Wait-free Wait-free 是指任意线程的任何操作都可以在有限步之内结束,而不用关心其它线程的执行速度。 Wait-free 是基于 per-thread 的,可以认为是 starvation-free 的。 非常遗憾的是实际情况并非如此,采用 Wait-free 的程序并不能保证 starvation-free,同时内存消耗也随线程数量而线性增长。目前只有极少数的非阻塞算法实现了这一点。 所有 Wait-free 的算法都是 Lock-Free 的。
为什么Wait-Free才是最优解? 相比于Lock-Free,Wait-Free设计更进一步,保证每个线程都能在有限的步骤内完成其操作,而不需要依赖竞争和重试逻辑。 这种确定性使得Wait-Free在性能和响应时间上都有显著优势,尤其适合高实时性、高并发的场景。 Wait-Free的核心在于,它不仅消除了锁,还消除了等待的概念。 虽然Wait-Free算法的实现复杂度更高,但它带来了更加稳定和高效的性能表现。 BqLog的Wait-Free实现 BqLog的消息队列bq::ring_buffer通过自创的一种算法实现了Wait-Free,该算法用fetch_add替代CAS,并且设计了一个空间不足时候的回滚算法 可能大家会有质疑,觉得这个回滚算法性能好像很差,也不再是Wait-Free的了。
wait-free & lock-free 原子指令能为我们的服务赋予两个重要属性:wait-free和lock-free。 如果我们的服务中使用了锁,那么OS可能把一个刚获得锁的线程切换出去,这时候所有依赖这个锁的线程都在等待,而没有做有用的事,所以用了锁就不是lock-free,更不会是wait-free。 在百度广泛又多样的在线服务中,对时效性也有着严苛的要求,如果RPC中最关键的部分满足wait-free或lock-free,就可以提供更稳定的服务质量。 值得提醒的是,常见想法是lock-free或wait-free的算法会更快,但事实可能相反,因为: lock-free和wait-free必须处理更多更复杂的race condition和ABA problem lock-free/wait-free算法的价值在于其保证了一个或所有线程始终在做有用的事,而不是绝对的高性能。
lock-free是目前最常见的无锁编程的实现级别(一共三种级别): wait-free lock-free obstruction-free 2. 为什么要 Non-blocking sync ? 3. wait-free 是最理想的模式,整个操作保证每个线程在有限步骤下完成。 保证系统级吞吐(system-wide throughput)以及无线程饥饿。 截止2011年,没有多少具体的实现。 wait-free的算法必定也是lock-free的。 5. obstruction-free 在任何时间点,一个线程被隔离为一个事务进行执行(其他线程suspended),并且在有限步骤内完成。
无锁算法的无等待模拟 Rust实现 这是 <<Crust of Rust>> 作者的最新的一期视频, 这一期视频中, 作者使用 Rust 实现了并发算法论文 <<A Practical Wait-Free
其中无锁编程(Lock-free)属于非阻塞同步(Non-blocking Synchronization)中的一种情况,实现非阻塞同步的算法方案按照效果要求不同可以粗略的分为: Wait-free: Lock-Free (LF) Wait-Free 5. Wait-Free (WF) 6. Wait-Free Bounded (WFB) 7. Wait-Free Population Oblivious (WFPO) 具体细节可以参考这篇文章http://ifeve.com/lock-free-and-wait-free/,有对阻塞非阻塞的等定义的详细描述
lock-free和wait-free的区别: 阻塞算法可能会出现整个系统都挂起的情况(占有锁的线程被中断,无法释放所,那么所有试图争用这个锁的线程会被挂起),系统中的所有线程全部饿死。
data tree 的所有 API 都是 wait-free 的,正在执行中的 API 调用不会影响其他 API 的完成。 data tree 的 API都是对文件系统的 wait-free 操作,不直接提供锁这样的分布式协同机制。但是 data tree 的 API 非常强大,可以用来实现多种分布式协同机制。
图 3:分布式环境中的(a)传统反向传播和(b)无等待(wait-free)反向传播。 ? 表 2:用于参数同步的 Poseidon API。 ? 图 4:Poseidon 架构的概览。 ?
虽然整体风格是UNIX的,但是在API操作的部分细节有所不同: ZNode的数据只支持全量写和读,不支持部分读写 所有的API都是Wait-Free——即正在做的API不会影响其他的API ZNode分类
data tree 的所有 API 都是 wait-free 的,正在执行中的 API 调用不会影响其他 API 的完成。 data tree 的 API都是对文件系统的 wait-free 操作,不直接提供锁这样的分布式协同机制。但是 data tree 的 API 非常强大,可以用来实现多种分布式协同机制。
不难得出 Obstruction-free 是 Non-blocking synchronization 中性能最差的,而 Wait-free 性能是最好的,但实现难度也是最大的,因此 Lock-free
无锁(Lock-Free)与无阻塞(Wait-Free)虽然锁无关操作筑牢了线程安全的防线,但它并不保证操作能瞬间完成。 无锁操作或许需要多次尝试才能达成目标,而无阻塞(Wait-Free)操作则更为严格,它承诺每个线程都能在有限的步骤内顺利完成操作,绝不会被其他线程阻挡前行。
MauriceHerlihy和NirShavit(相比《多处理器编程艺术》)区分了三种类型的无阻塞数据结构,每种均具有不同的特性: data structures are wait-free, if every The definition of ‘lock-free’and ‘wait-free’ only mention the upper bound of an operation. Single-Producer/Single-Consumer Queue 无等待单生产者/单消费者队列 The boost::lockfree::spsc_queue classimplements a wait-free
wait-free:需要取得锁的线程在有限步骤或时间内就可以成功,任意线程都会成功,语义更加强烈。
6.5 无等待(Wait-Free) 无等待的情况下,所有的操作都必须在有限步内完成。 可以分为“有界无等待”和“线程无关的无等待”,区别在于循环次数的限制不同。
基于CAS的“wait-free”(常规无等待)来实现,CAS并不是一个算法,它是一个CPU直接支持的硬件指令,这也就在一定程度上决定了它的平台相关性。
Wait-Free 同步和事务内存 由于锁同步会导致性能降级和优先级翻转等问题。 wait-free同步[22]通过确保一个事务能够在有限的步骤内完成访问共享资源的操作(无需关心其他事务的执行进度)来解决这些问题。 然而,很难设计采纳这种技术的程序,同时有可能找不到实现了wait-free的算法。再者,wait-free同步要求硬件支持原子语义。 例如,双边队列的wait-free实现要求实现Double CompareAnd-Swap (DCAS),而大多数处理器不支持这类实现。 事务内存是另一种解决这类锁竞争的方式。 Herlihy, “Wait-free synchronization,” ACM Trans. Program. Lang.
ConcurrentLinkedQueue 采用非阻塞的方式实现线程安全队列,它采用了"wait-free"算法(即CAS算法)来实现。 ?