多处理器编程的艺术 第二版 chapter 5 第五章 笔记
前言
本章目的:构造一些原语,希望我们能利用这些原语来解决一些同步问题。
consensus: 一个同步原语的共识数是它能解决共识问题的最大线程数
上面的无法通过下面的synchronization primitives实现出来。
注意,这里的逻辑是,我们制造出来一些同步工具/同步原语,然后利用共识数来对这些同步工具进行测试,看看这些同步工具厉不厉害。我们制造出同步工具不是专门为共识数测试设计的,但共识数就像一个“能力试炼场”,能测出它们的强弱。测试结果好的(共识数高),就可以放心拿去其他并发场景用,比如构建复杂数据结构或协议。
5.1 Consensus numbers
每个线程calls the decide() method with its input v at most once. 最多一次
•consistent: all threads decide the same value,
•valid: the common decision value is some thread’s input.
被选中的要先完成。比如v被选中了,那么执行v的线程先完成了decide()方法调用。
每一个线程只执行一次,线程数量有限,lock-free告诉我们总有一些线程在前进,那最终所有的线程也会执行完毕,所以lock-free也是wait-free。
在wait-free下实现consensus:consensus protocol
Definition 5.1.1. 我们说,class C 解决了 n线程公式问题 如果:使用了任意数量的C的对象+任意数量的寄存器,能够实现n线程consensus protocol
Definition 5.1.2. The consensus number of a class C is the largest n for which that class solves n-thread consensus. If no largest n exists, we say the consensus number of the class is infinite.
Corollary 5.1.3. Suppose one can implement an object of class C from one or more objects of class D, together with some number of atomic registers. If class C solves n-consensus, then so does class D.
5.1.1 States and valence
initial:move之前的ps
The decision value of any final state is the value decided by all threads in that state.
注意上面这句话,无论在之前经过多少次move,在最后状态下,所有的线程应该有一个decision value,这个值应该是一样的。
bivalent:后代最后结果有0有1
univalent:只有一个
(书上的那个图,不需要管0和1哪来的,直接往上面推就行了)
其实有点像几个人往棋盘(共享变量)下棋,在某个阶段,游戏可能已经结束,大家达成共识谁赢谁输。每次只能有一个人下棋(满足sequential)
Lemma 5.1.5. Every n-thread consensus protocol has a bivalent initial state.
跟两个线程一样证,比较显然
critical:几个人下棋,总有一天要有一个结果,那么下出了得出结果的前一步棋的那个状态就是critical state,后续下的棋对结果没什么影响,已经收敛。
5.2 Atomic registers
atomic registers 做不到吗
Theorem 5.2.1. Atomic registers have consensus number 1.
运行系统直到critical state
3种情况:1. 一个线程要从一个寄存器读; 2. 两个线程往两个reg写; 3.两个线程往一个reg写
1. A线程读,对于B来说,在A读之前和A读之后,他根本不知道有什么区别(state完全一致,看不到A的本地值),又要怎么得出两个不同的decision value呢?不成
2. A,B往两个reg写,无论什么顺序,same protocol state(而我们假定,在相同的state,应该会有一样的树,结果也是一样的),所以就不是critical state,也不成
3. A写完B直接覆盖,与B自己写,两种state对于B来说完全一样,也是不可能得出两个不同的decision value。
Corollary 5.2.2. It is impossible to construct a wait-free implementation of any object with consensus number greater than 1 using atomic registers.
5.3 Consensus protocols
协议让线程来提交自己的值,然后再自定义一个decide的实现
5.4 FIFO queues
原文比较完整,此处补充
Theorem 5.4.3. FIFO queues have consensus number 2
的第二种情况的图
以及
C都是分不清的,state完全一样
5.5 Multiple assignment objects
Theorem 5.5.1. There is no wait-free implementation of an (m,n)-assignment object by atomic registers for any n > m > 1.Proof. It is enough to show that we can solve 2-consensus given two threads and
a (2,3)-assignment object. (Exercise 5.26 asks you to justify this claim.)
这里的意思应该是,(2,3)-assignment object都有共识数2了,那前面已经说了atomic register大于一的全都造不出来
Corollary 5.2.2. It is impossible to construct a wait-free implementation of any ob-
ject with consensus number greater than 1 using atomic registers.
比较严谨的是,要证明(2,3)在所有(m,n)中是最弱的。这是显然的(应该),因为所有的(m,n)都可以分出来2,3的功能,比如3,4,天然就需要提供3格位置,2次的数据填充。
所以得证。注意这里用的是lock-base的操作,属于是作弊了,不是wait-free。
对Theorem 5.5.2的证明比较有意思,5.5.2的意思是,(n,n(n+1)/2)-assignment有能力让n个线程取得共识,具体是这样做的,将n(n+1)/2分成两部分,第一部分是0~n-1,对应n个线程,这些寄存器是私有的,只有线程自己写。剩下就是n(n+1)/2-n=n(n-1)/2个线程,给他们编上号,比如n=3,n(n-1)/2就是3个,编号r10, r20, r21。0号assign,就会给r0, r10, r20三个寄存器放入自己投票的值。
对于case2,d写的东西全都在,一看就是最后写的,淘汰掉。a跟b对应的0,1,我们看r10寄存器,发现写的a,所以a也在b之前写入的,所以就是1号最先写入。
这里的严格证明就不补了。
5.6 Read–modify–write operations
RMW操作一般是cpu内置操作。
5.7 Common2 RMW operations
普通2号RMW的问题,在于虽然赢家知道自己赢了,输家知道自己输了,但是输家不知道谁赢,自然也找不到共识数了。
比如说,getAndIncrement(),a先到,v+1,b再到,v+2,c一看数字v+2傻眼了,是a还是b最早到的?(对应fA(fB(v)) = fB(fA(v)))
再比如,getAndSet(),c一看里面存的b,也是傻眼,虽然知道了自己已经来晚了,但是,有可能b先到了,a还在自己后面呢(b是胜者)?也有可能a先到,b再到,把a的数值给覆盖了(a是胜者)?(对应fi(fj(v)) = fi(v) or fj(fi(v)) = fj(v).)
5.8 The compareAndSet operation
并发大王,CMPXCHG。
5.8.1:compareAndSet()+get() = infinite consensus number
线程A如果返回了true,那么在线性化时间顺序上,A就是第一个拿到结果的,如果false,就去获取胜者的结果。
Corollary 5.8.2,不需要get(),我的理解是,不妨保存下来总共的线程数,比如10个线程,我们有int t = 10, 然后循环for i = 0~9 {if(compareAndSet(i, i)){return proposed[i];}}无非就是挨个检查到底是谁的线程id。
当然只是麻烦一点而已了。