Art of Multiprocessor Programming 答案 ch17

本文讨论了多核编程中关于线程同步的问题,分析了在特定情况下线程行为不确定性和同步算法的正确性。通过举例说明了树形结构在同步过程中的缺陷,以及如何通过归纳法证明线程同步的轮数。同时,提到了不同类型的树在并发操作中的性能表现,如组合树、静态树和分发树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

198. 如果线程 k + 2^r 比 线程  k 快很多,当线程  k + 2^r 读 a[k]的时候将读到没有 sum k段的原始值,没有求和的作用。

public void run() {
 int d = 1, sum = 0;
 while (d < N) {
  if (i >= d)
    sum = a[i-d];
  b.await(); 
  if (i >= d)
   a[i] += sum;
  b.await();
  d = d * 2;
}}}
代码来自书作者ppt。

199. 

package p199;

import java.util.concurrent.atomic.AtomicInteger;

public class SenseBarrier 
{
	private AtomicInteger count;
	private final int size;
	private boolean sense;
	private ThreadLocal
  
    threadSense;
	
	public SenseBarrier(int n)
	{
		count = new AtomicInteger(n);
		size = n;
		sense = false;
		threadSense = new ThreadLocal
   
    ()
				{
					protected Boolean initialValue() { return !sense; };
				};
	}
	
	public void await()
	{
		boolean mySense = threadSense.get();
		int position = count.getAndDecrement();
		
		if(position == 1)
		{
			count.set(size);
			sense = mySense;
			
			this.notifyAll();
		}else
		{
			while(sense != mySense)
			{
				try
				{
					this.wait();
				}catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
		
		threadSense.set(!mySense);
	}

}

   
  

当参与的线程行为不确定性强,响应时间要求不高,cpu不希望独占是采用wait的方式合适;否则采用spin比较合适。


200.

package ch17.basic;

import java.util.concurrent.atomic.AtomicInteger;

public abstract class TreeNode 
{
	protected AtomicInteger count;
	protected TreeNode parent;
	protected volatile boolean sense;
	protected final int radix;
	
	public TreeNode(int radix)
	{
		sense = false;
		parent = null;
		this.radix = radix;
		count = new AtomicInteger(radix);
	}
	
	public TreeNode(int radix, TreeNode parent)
	{
		this(radix);
		this.parent = parent;
	}
	
	public void nodeNotify()
	{
		return;
	}
	
	abstract public void await();
	
	abstract public void build(int depth, Integer leaves, TreeNode leaf[]);
	
}

package ch17.p200;

import ch17.basic.Barrier;
import ch17.basic.TreeNode;

public class RunnableTree implements Barrier 
{
	final int radix;
	Node[] leaf;
	int leaves;
	ThreadLocal
  
    threadSense;
	ThreadLocal
   
     ThreadID;
	Runnable task;
	
	final int n;
	
	public RunnableTree(int n, int radix, Runnable task)
	{
		this.radix = radix;
		this.n = n;
		leaves = 0;
		
		threadSense = new ThreadLocal
    
     ()
				{
					protected Boolean initialValue() { return true; };
				};
		ThreadID = new ThreadLocal
     
      ()
				{
					protected Integer initialValue() { return 0; };
				};
		
		this.task = task;
		
		build();
	}
	
	public void await()
	{
		int me = ThreadID.get();
		Node myNode = leaf[me / radix];
		myNode.await();
	}
	
	private void build()
	{
		leaf = new Node[n / radix];
		
		int depth = 0;
		int layNum = n;
		while(layNum > 1)
		{
			depth ++;
			layNum = layNum / radix;
		}
		
		Node root = new Node(radix);
		Integer leaveNum = new Integer(0);
		root.build(depth, leaveNum, leaf);
		leaves = leaveNum;
	}
	
	
	
	private class Node extends TreeNode
	{	
		public Node(int radix)
		{
			super(radix);
		}
		
		public Node(int radix, Node myParent)
		{
			super(radix, myParent);
		}
		
		public void build(int depth, Integer leaves, TreeNode leaf[])
		{
			if(depth < 0)
			{
				return;
			}
			if(depth == 0)
			{
				leaf[leaves] = this;
				leaves ++;
				return;
			}
			
			for(int i = 0; i < radix; i ++)
			{
				Node node = new Node(radix, this);
				node.build(depth - 1, leaves, leaf);
			}
		}
		
		public void await()
		{
			boolean mySense = threadSense.get();
			int position = count.getAndDecrement();
			
			if(position == 1)
			{
				if(parent != null)
				{
					parent.await();
				}else
				{
					task.run();
				}
				count.set(radix);
				sense = mySense;
			}else
			{
				while(sense != mySense) {}
			}
			threadSense.set(!mySense);
		}
	}
}

     
    
   
  

201.

package ch17.p201;

import ch17.basic.TreeNode;

public class GeneralTree
{
	protected final int radix;
	protected TreeNode[] leaf;
	protected int leaves;
	protected ThreadLocal
  
    ThreadID;
	
	protected final int n;
	protected TreeNode root;
	
	//The inherited class should have root initiated
	public GeneralTree(int n, int radix, TreeNode root)
	{
		this.radix = radix;
		this.n = n;
		leaves = 0;
		
		ThreadID = new ThreadLocal
   
    ()
				{
					protected Integer initialValue() { return 0; };
				};		
				
		this.root = root;
		build();
	}
		
	public void await()
	{
		int me = ThreadID.get();
		TreeNode myNode = leaf[me / radix];
		myNode.await();
	}
	
	protected void build()
	{
		leaf = new TreeNode[n / radix];
		
		int depth = 0;
		int layNum = n;
		while(layNum > 1)
		{
			depth ++;
			layNum = layNum / radix;
		}
		
		Integer leaveNum = new Integer(0);
		root.build(depth, leaveNum, leaf);
		leaves = leaveNum;
	}
	
}

   
  


202.

package ch17.p202;

public class TourBarrier
{
	TourNode[] nodes;
	TourNode[] leaf;
	ThreadLocal
  
    ThreadID;
	ThreadLocal
   
     threadSense;
		
	public TourBarrier(int n)
	{
		ThreadID = new ThreadLocal
    
     ()
		{
			protected Integer initialValue() { return 0; };
		};	
	    this.threadSense = new ThreadLocal
     
      () {
	    	protected Boolean initialValue() { return true; };
	    };
	    
		int nodeNum = (1 << n) - 1;
		nodes = new TourNode[nodeNum];
		int rootIndex = 0;
		nodes[rootIndex] = new TourNode(rootIndex);
		nodes[rootIndex].build(n - 1, nodes);
		
		int leafNum = (1 << (n - 1));
		leaf = new TourNode[leafNum];
		for(int i =0; i < leafNum; i ++)
		{
			leaf[leafNum - i - 1] = nodes[nodeNum - i - 1];
		}
	}
	
	public void await()
	{
		int me = ThreadID.get();
		TourNode myLeaf = leaf[me / 2];
		boolean sense = threadSense.get();
		myLeaf.await(sense);
		threadSense.set(!sense);
	}
	
	
	private class TourNode
	{
		final int myIndex;
		volatile boolean flag;
		boolean active;
		int parentIndex;
		int partnerIndex;
		
		public TourNode(int index)
		{		
			this.myIndex = index;
			this.flag = false;
			this.active = false;
			this.parentIndex = -1;
			setPartnerIndex();
		}
		
		public TourNode(int parentIndex, int index)
		{			
			this(index);
			this.active = true;
			this.parentIndex = parentIndex;
		}
		
		private void setPartnerIndex()
		{
			if(myIndex % 2 == 0)
			{
				this.partnerIndex = myIndex + 1;
			}else
			{
				this.partnerIndex = myIndex - 1;
			}
		}
		
		public int getIndex()
		{
			return myIndex;
		}
		
		public void build(int depth, TourNode nodes[])
		{
			if(depth < 0)
			{
				System.out.println("Invalid depth");
				return;
			}else if(depth == 0)
			{
				return;
			}else
			{
				int leftIndex = this.myIndex * 2 + 1;
				int rightIndex = (this.myIndex * 2) + 2;
				
				nodes[leftIndex] = new TourNode(this.getIndex(), leftIndex);
				nodes[rightIndex] = new TourNode(rightIndex);
				
				nodes[leftIndex].build(depth - 1, nodes);
				nodes[rightIndex].build(depth - 1, nodes);
			}
		}
		
		public void await(boolean sense)
		{
			if(active)
			{
				if(parentIndex >= 0)
				{
					while(flag != sense) {}
				
					nodes[parentIndex].await(sense);
					nodes[partnerIndex].flag = sense;
				}
			}else
			{
				nodes[partnerIndex].flag = sense;
				while(flag != sense){}
			}
		}
	}
	
}

     
    
   
  

203.

这是不对的.因为每次在特定节点上竞争的线程是不固定的.比如,4个线程的树, 第一次由线程1和线程3到达root,假设所有ThreadLocal初始值为false,则线程1和3从root返回后mySense = true;同时线程2和线程4在root上的mySense=false;root的sense = false。第二次barrier由线程2和4到达,线程2发现sense == mySense,于是没有与线程同步而直接从root返回。


204.

这种树的算法是不正确的。

如果障碍的用法是先执行操作,然后等待在树上,则有可能出现的情形是,左子树的所有节点都已经完成操作,右子树的所有Active节点都到达parent直至root,但是有的passive节点还没有完成phaseI的操作 ==》左子树节点都从root向下返回,并开始phaseII的操作。这是可能会涉及与右子树节点的交互,并在不同步的状态下得到结果作为phaseII的结果。


如果障碍的用法是先在树上等待返回再执行操作:障碍的正确性在于如果不是所有的线程都完成了phase1则不能开始phase2。但是这个算法只能保证如果不是所有线程都开始phase1则不能开始phase2。比如这样的情形:左右子树的所有节点都已经开始执行phase1==》所有的active节点都完成了phase1的操作==》所有active节点向root回溯 ==》根结点的左子树都已经完成phase1 ==》根结点的左子树的所有节点都开始执行phase2但是右子树还有节点正在执行phase1 ==》不同步的状态。


205.

package ch17.p205;

import counting.Bitonic;

public class CounterBarrier implements ch17.basic.Barrier
{
	private Bitonic counter;
	private volatile boolean sense;
	private ThreadLocal
  
    ThreadId;
	private ThreadLocal
   
     threadSense;
	private final int size;
	
	public CounterBarrier(int size)
	{
		this.size = size;
		this.sense = false;
		
		this.counter = new Bitonic(size);
		
		ThreadId = new ThreadLocal
    
     ()
		{
			protected Integer initialValue() { return 0; };
		};	
	    this.threadSense = new ThreadLocal
     
      () {
	    	protected Boolean initialValue() { return (!sense); };
	    };
	}
	
	public void await()
	{
		int myId = ThreadId.get();
		boolean mySense = threadSense.get();
		
		int output = counter.traverse(myId);
		if((output % this.size) == 0)
		{
			sense = !sense;
		}else
		{
			while(mySense != this.sense) {}
		}
		threadSense.set(!mySense);
	}
	
}

     
    
   
  


206.

package ch17.p206;

import register.WFSnapshot;

public class SnapshotBarrier implements TDBarrier 
{
	private WFSnapshot
  
    snapshot;
	
//	ThreadLocal
   
     ThreadId;
	
	public SnapshotBarrier(int capacity)
	{
		snapshot = new WFSnapshot
    
     (capacity, false);
	}
	
	public void setActive(boolean state)
	{
		snapshot.update(state);
	}

	public boolean isTerminated()
	{
		Boolean[] results = snapshot.scan();
		for(int i = 0; i < results.length; i ++)
		{
			if(results[i])
			{
				return false;
			}
		}
		return true;
	}
}

    
   
  

207.

可以用归纳法证明,当一个线程完成了第i步,即已经收到了i-(2^r)(mod n)的通知 ,则线程k已经与线程 k - 1, k - 2, ..., k - (2 ^ i ) + 1 同步了。

假设在i 时为真,则 i + 1时,线程k在i是已经与前 (2 ^ i)个线程同步了,同时线程k与线程( k - (2 ^ i))同步,线程( k - (2 ^ i)) 已经与线程 (k -(2 ^ i)), (k - (2 ^ i) - 1), ... , (k - (2 ^ (i + 1)) + 1) 同步了。得证。

所以要所有的线程都与其他的线程同步,需要 logn轮同步。


如果线程数不是2 ^ i,也没有关系,只是线程k在同步时,同步的前第1到第n个线程,与第n+1到2n个线程会有重叠的部分。但是并不会有重复的消息,只要能够消息能够区分是针对哪一步。因为先发后收,也不会有死锁。


208.

package ch17.p208;


public class DisBarrier implements ch17.basic.Barrier
{
	private final int size;
	private final int powSize;
	volatile private boolean[][] flags;
	private ThreadLocal
  
    ThreadId;
	private ThreadLocal
   
     flag;
	
	public DisBarrier(int powSize)
	{
		this.powSize = powSize;
		this.size = (int)Math.pow(2, powSize);
		
		flags = new boolean[size][];
		for(int i = 0; i < size; i ++)
		{
			//initialized as all false
			flags[i] = new boolean[powSize];
		}
		
		ThreadId = new ThreadLocal
    
     ()
		{
			protected Integer initialValue() { return 0; };
		};
		
		flag = new ThreadLocal
     
      ()
		{
			protected Boolean initialValue() { return true; };
		};		
	}
	
	public void await()
	{
		int myId = ThreadId.get();
		boolean myFlag = flag.get();
		
		int step = 1;
		for(int i = 0; i < powSize; i ++)
		{
			int toIndex = (myId + step) % size;
			flags[toIndex][i] = myFlag;
			
			while(flags[myId][i] != myFlag) {}
			step <<= 1;
		}
		flag.set(!myFlag);
	}
}

     
    
   
  


209.

设线程数为 n = (r ^ d),  树为r叉树,一共有(1 - (r ^d)) / (1 - r)个节点。


组合树:每个节点经过了r个getAndDecrement操作,2个反转操作(setCounter, !sense)和r个 threadSense反转操作。

静态树:每个节点经历了r个递减操作和r个threadSense反转操作。

分发树:每个线程经历了 logn轮的操作,每轮包括一个发消息操作和一个收消息操作。

更具体的分析见 barrier


210.

package ch17.p210;

import java.util.concurrent.atomic.AtomicInteger;

import programm.ch17.Barrier.src.barrier.TDBarrier;

public class ActiveTDBarrier implements TDBarrier
{
	 AtomicInteger count;
	 ThreadLocal
  
    ThreadId;
	 final int capacity;
	 AtomicInteger totalActivated;
	 Runnable[] tasks;
	 DEQueue[] queues;
	 
	 public ActiveTDBarrier(int n, DEQueue[] queue, Runnable[] tasks) 
	 {
		 this.count = new AtomicInteger(0);
		 this.capacity = n;
		 this.queues = queue;
		 this.tasks = tasks;
		 totalActivated = new AtomicInteger(0);
		 ThreadId = new ThreadLocal
   
    ()
		{
			 protected Integer initialValue() { return 0; };
		};		// TODO Auto-generated constructor stub
	}

	 public void setActive(boolean active)
	 {
		 if(active)
		 {
			 count.getAndIncrement();
			 totalActivated.getAndIncrement();
		 }else
		 {
			 count.getAndDecrement();
		 }
	 }
	 
	 public boolean isTerminated()
	 {
		int localActivated = totalActivated.get();
		int checkNum = 0;
		
		while(checkNum < 2)
		{
			if(count.get() != 0)
			{
				return false;
			}
		
			if(localActivated != totalActivated.get())
			{
				return false;
			}
			//No one moves in between count reading
			//task may be pushed in queue or popped out the queue
		
			for(int i = 0; i < queues.length; i ++)
			{
				if(!queues[i].isEmpty())
				{
					return false;
				}
			}
			//task my be pushed in queue or popped out the queue or running
		
			for(int i = 0; i < tasks.length; i ++)
			{
				if(tasks[i] != null)
				{
					return false;
				}
			}
			//task may be popped out the queue or new task pushed into queue

			/*After first check, the new task may be 
				in queue 
				or in task 
				or had finished
			They could be detected by 
				queue check
				or task check 
				or totalActivated 
			 */
			checkNum ++;
		}
		
		return localActivated == totalActivated.get();
	 }
}

   
  
package ch17.p210;

import java.util.Random;

public class ActiveTDThread 
{
	  DEQueue[] queue;
	  ActiveTDBarrier tdBarrier;
	  Runnable[] tasks;
	  Random random;
	  final static int QueueSize = 64;
	  
	  public ActiveTDThread(int n) {
	    queue = new DEQueue[n];
	    tasks = new Runnable[n];
	    tdBarrier = new ActiveTDBarrier(n, queue, tasks);
	    random = new Random();
	    for (int i = 0; i < n; i++) {
	      queue[i] = new DEQueue(QueueSize);
	    }
	    for(int i = 0; i < n; i ++)
	    {
	    	tasks[i] = null;
	    }
	  }
	  
	  public void run() 
	  {
	    int me = ThreadID.get();
    	tdBarrier.setActive(true);
	    tasks[me] = queue[me].popBottom(); // attempt to pop 1st item
	    while (true) 
	    {
		    while (tasks[me] != null) 
		    { // if there is an item
		    	tasks[me].run();      // execute it and then
		    	tasks[me] = queue[me].popBottom(); // pop the next item
		    }
	        tdBarrier.setActive(false); // no work
	        while (tasks[me] == null) 
	        { // steal an item
	        	int victim = random.nextInt(queue.length);
	        	if (!queue[victim].isEmpty()) 
	        	{
	        		tdBarrier.setActive(true);  // tentatively active
	        		tasks[me] = queue[victim].popTop();
	        		if (tasks[me] != null) 
	        		{
	        			tdBarrier.setActive(true);
	        		}
	        	}
	        	if (tdBarrier.isTerminated()) 
	        	{
	        		return;
	        	}
	        }
	    }
	 }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值