Art of Multiprocessor Programming 答案 ch6

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值