Art of Multiprocessor Programming 答案 ch6

来源:互联网 发布:网络电视需要多少网速 编辑:程序博客网 时间:2024/04/30 08:13

77. 将确定顺序规范的对象变成确定顺序规范: 

做不确定顺序规范到确定顺序规范的映射或者将Node变成ConsensusNode,即给Node一个consensus wrapper,保证不确定的输出将在各个线程中的道一致的确定的输出。

78. 无等待的算法会出错。seq == 0会被当作需要加到list当中的Node ==> tail->next = tail.

79. 不明白“不用通用构造”又“改造这个算法”是什么意思。还是用的通用构造

package p79;import java.util.concurrent.atomic.AtomicReference;public class Consensus<T>{private AtomicReference<T> curObj;public Consensus(){this.curObj = new AtomicReference<T>();curObj.set(null);}public T decide(T next){curObj.compareAndSet(null, next);return curObj.get();}}

package p79;public class Node {public Node next;public int seq;public int exp;public int v;public Consensus<Node> decideNext;public Node(){this.seq = 0;this.next = null;decideNext = new Consensus<Node>();}public Node(int exp, int replace){this();this.exp = exp;this.v = replace;}public static Node max(Node[] array){Node max = array[0];for(int i = 1; i < array.length; i ++){if(max.seq < array[i].seq){max = array[i];}}return max;}}
package p79;public class Universal_CAS{private Node[] announce;private Node[] head;private Node tail;private int n;private static ThreadLocal<Integer> ThreadId = new ThreadLocal<Integer>(){protected Integer initialValue(){return new Integer(0);}};public Universal_CAS(int n){this.n = n;tail = new Node();tail.seq = 1;announce = new Node[n];head = new Node[n];for(int i = 0; i < n; i ++){announce[i] = tail;head[i] = tail;}}public int read(){Node max = Node.max(head);return max.v;}public boolean compareAndSet(int exp, int replace){ int i = ThreadId.get(); announce[i] = new Node(exp, replace); head[i] = Node.max(head); Node before = null; while(announce[i].seq == 0) { before = head[i]; Node help = announce[(before.seq + 1) % n]; Node prefer = help; if(help.seq != 0) { prefer = announce[i]; }  Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; if(before.v != after.exp) { after.v = before.v; }  head[i] = after; }  boolean rc = false; if(before.v == announce[i].exp) { announce[i].v = replace; rc = true; }else { announce[i].v = before.v; } head[i] = announce[i]; return rc;}}


80. 如果每个线程首先尝试加入自己的结点,在announce[i].seq !=0 后就推出apply()方法,仍然会出现有的线程一直成功而有的线程饿死的状况。如果线程成功的加入自己的结点后开始尝试帮助其它的线程,例如将While()中的帮助循环n-1次,应该是可以的。

81. 将max方法改成下面这样,然后用max(tail)来得到head[i]

package p79;public class Node {public Node next;public int seq;public int exp;public int v;public Consensus<Node> decideNext;public Node(){this.seq = 0;this.next = null;decideNext = new Consensus<Node>();}public Node(int exp, int replace){this();this.exp = exp;this.v = replace;}public static Node max(Node tail){Node pre = tail;while(pre.next != null){pre = pre.next;}return pre;}}

82.

1. 因为有可能节点 seq已经设置,但是节点没有放到head[]中,

所以实际上的start(A) = m + 1,但是head[A].seq用Node.max()计算出来为m,

使6.4.4不成立。

2.仍然能够正常工作。

因为这种情况下有(head[A].seq - start(A)) >= -1。

假设(head[A].seq - start(A)) < -1,比如 = -2;则必然在head[A]后至少有2个节点增加到链表中。假设他们的seq为m + 1, m + 2。

如果设置 m +2的线程从 head[]中得到m + 1的值,则这个不等式成立;

如果从链表中得到m + 1的值,则这个线程必然已经在while()循环中完成了after.seq = m + 1,head[i] = after的一轮操作,即m + 1已经被这个线程加到head中。

所以这个不等式总是成立。

根据这个不等式,和Theorem 6.4.1的推导方法,线程经过了最多(n + 1)次迭代后仍然能将自己的announce设置到head中。



83.

 利用新添加的before和新添加的calculated flag。和java 垃圾搜集。tail的calculated初始化为true。
当用before.next = after构建链表时,也用 after.before = before构建双向链表。
完成将announce添加到链表的操作后,计算log值时,不从tail开始,而是从current向before利用反向链表,直到找到一个before.calculated = true的node,作为初始值开始计算本线程的local log。
当head[i]的值计算完毕,calculated设置完毕后head[i].before = null;使节点脱离反向链表。
但是如果一个线程中途退出了,它将由head[i] hold住无限长的 next链表。所以其他线程也要帮助它脱离next链表。
准备一个全局的dummy node,它将作为next的填充物,防止before.next.decide在脱链后又被加入新的节点。
每个线程在计算完自己的log后帮助其他线程的过时head脱链:
检查每个head[j].seq。如果(current.seq - head[j].seq) >( n + 1);假设head[j].seq = m,则必然已经有一个node.seq = (m + 1)的calculated = true。即这个node已经不需要了。所以将head[j].next = dummy。
但是其他的线程在沿着链表遍历的时候可能会遇到这个节点。如果使用的是这个节点本身,或者它的before/next节点值,线程都能够发现这个节点的异常,比如after = before.next.decide() ; after == dummy。线程可以重新计算max重来。
因为这个处理并不影响线程的seq关系,所以无等待没有影响。
这个过时的节点可能会被附上新的before节点,但是当before关系被用到时,这个节点自身的calculated已经被置位,所以不会有被反向链表用到的时候。而新被链入的before节点在这个过时节点本身从next链中摘除并回收的时候,新加入的before节点也会被解除被引用的关系。


84.

0 0
原创粉丝点击