Non-Blocking stack和Block stack的插入时间对比

本文实现了一个基于非阻塞算法和一个基于阻塞算法的栈,并通过多线程环境下的大量插入操作来比较这两种算法的时间性能差异。

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

本文通过对实现了两个Stack,一个是基于非阻塞(Non-Blocking)算法,另一个是基于阻塞算法(Blocking)的,然后对这两个队列进行多线程环境下的大量Insert操作,通过比较二者的消耗时间,进而比较基于Volatile和CAS轮询操作的非阻塞算法和基于锁的阻塞算法在时间上的性能差异究竟有多大。

 

基于非阻塞算法的Stack

 

public class ConcurrentStack<E> {

	AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();
	
	public void push(E item)
	{
		Node<E> newHead = new Node<E>(item);
		Node<E> oldHead;
		
		do
		{
			oldHead = top.get();
			newHead.next = oldHead;
		}while(!top.compareAndSet(oldHead, newHead));
	}
	
	public E pop()
	{
		Node<E> newHead;
		Node<E> oldHead;
		
		do
		{
			oldHead = top.get();
			if(oldHead == null)
			{
				return null;
			}
			
			newHead = oldHead.next;
		}while(!top.compareAndSet(oldHead, newHead));
		
		return oldHead.item;
	}
	
	private static class Node<E>
	{
		public final E item;
		public Node<E> next;
		
		public Node(E item)
		{
			this.item = item;
		}
	}
}

 

  阻塞算法的Stack

 

public class BlockingStack<E> {
	
	Node<E> top = new Node<E>(null);
	
	public synchronized void push(E item)
	{
		Node<E> newHead = new Node<E>(item);
		Node<E> oldHead;
		
		oldHead = top;
		newHead.next = oldHead;
		top = newHead;
	}
	
	public synchronized E pop()
	{
		Node<E> newHead;
		Node<E> oldHead;
		
		if(top == null)
		{
			return null;
		}
		oldHead = top;
		newHead = top.next;
		top = newHead;
		
		return oldHead.item;
	}
	
	private static class Node<E>
	{
		public final E item;
		public Node<E> next;
		
		public Node(E item)
		{
			this.item = item;
		}
		
	}
}

 

  测试代码

 

public class TestNonAndBlockingStack {
	
	private int insertNums;
	
	public TestNonAndBlockingStack(int insertNums)
	{
		this.insertNums = insertNums;
	}

	class NonBlockStackThread extends Thread
	{
		private ConcurrentStack<String> stack;
		private CountDownLatch startGate;
		private CountDownLatch endGate;
		
		public NonBlockStackThread(ConcurrentStack<String> stack,
								   CountDownLatch startGate,
								   CountDownLatch endGate)
		{
			this.stack = stack;
			this.startGate = startGate;
			this.endGate = endGate;
		}
		
		public void run()
		{
			try {
				startGate.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for(int i = 0; i < insertNums; i++)
			{
				stack.push("olylakers");
			}
			endGate.countDown();
		}
	}
	
	class BlockStackThread extends Thread
	{
		private BlockingStack<String> stack;
		private CountDownLatch startGate;
		private CountDownLatch endGate;
		
		public BlockStackThread(BlockingStack<String> stack,
								CountDownLatch startGate,
								CountDownLatch endGate)
		{
			this.stack = stack;
			this.startGate = startGate;
			this.endGate = endGate;
		}
		
		public void run()
		{
			try {
				startGate.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for(int i = 0; i < insertNums; i++)
			{
				stack.push("oly");
			}
			
			endGate.countDown();
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int threadNums = 100;
		int insertNums = 50000;
		TestNonAndBlockingStack test = new TestNonAndBlockingStack(insertNums);
		
		CountDownLatch startGateN = new CountDownLatch(1);
		CountDownLatch endGateN = new CountDownLatch(threadNums);
		ConcurrentStack<String> concurrentStack = new ConcurrentStack<String>();
		
		CountDownLatch startGateB = new CountDownLatch(1);
     	CountDownLatch endGateB = new CountDownLatch(threadNums);
     	BlockingStack<String> blockingStack = new BlockingStack<String>();
     	
     	for(int i = 0; i < threadNums; i++)
     	{
     		test.new NonBlockStackThread(concurrentStack, startGateN, endGateN).start();
     	}
     	
     	long startTime = System.currentTimeMillis();
     	startGateN.countDown();
     	try {
			endGateN.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("The concurrent stack push cost: "+ (System.currentTimeMillis() - startTime));
     	
     	for(int i = 0; i < threadNums; i++)
     	{
     		test.new BlockStackThread(blockingStack, startGateB, endGateB).start();
     	}
     	
     	startTime = System.currentTimeMillis();
     	startGateB.countDown();
     	try {
			endGateB.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("The blocking stack push cost: "+ (System.currentTimeMillis() - startTime));				
	}
}

 

  当 threadNums = 100, insertNums = 50000时,跑两次的测试结果为:

The concurrent stack push cost: 2125 The blocking stack push cost: 3657

 

 

        The concurrent stack push cost: 1953

        The blocking stack push cost: 3078

 

 

 

  当 threadNums = 500, insertNums = 10000时,跑两次的测试结果为:

        The concurrent stack push cost: 2141 The blocking stack push cost: 3219

 

 

The concurrent stack push cost: 2171

The blocking stack push cost: 3344

 

测试机器为:Core2(TM)2 Quad CPU Q9500 @2.83GHz, 4核。内存:3.25G。

 

 

 

 

 

<think>我们分析日志输出代码逻辑:日志信息:[SD0]Non-blockingRWdoesnotallowblkcnt(16)>maxblks(65536)等,一共四条,其中blkcnt分别为16,1,2,1,而maxblks都是65536。代码中与非阻塞读写检查相关的部分:#ifdefFEATURE_NONBLOCKING_RWif(blkcnt>maxblks){msdc_pr_err("[SD%d]Non-blockingRWdoesnotallowblkcnt(%d)>maxblks(%d)\n",host->id,blkcnt,maxblks);BUG_ON(1);}#endif同时,我们在函数开头还看到一条打印(非条件编译内):msdc_pr_err("[SD%d]Non-blockingRWdoesnotallowblkcnt(%d)>maxblks(%d)\n",host->id,blkcnt,maxblks);注意:这条打印语句是在条件编译之外,也就是说无论是否定义了FEATURE_NONBLOCKING_RW,都会打印。但是,错误检查(BUG_ON(1))只在定义了FEATURE_NONBLOCKING_RY时才执行。日志中打印的blkcnt(最大16)都远远小于maxblks(65536),因此不应该触发这个错误。为什么会打印这些日志?因为代码中在函数开头有一句打印:msdc_pr_err("[SD%d]Non-blockingRWdoesnotallowblkcnt(%d)>maxblks(%d)\n",host->id,blkcnt,maxblks);这条打印语句没有在任何条件判断内,也就是说,每次调用这个函数,无论blkcnt是否大于maxblks,都会打印这条错误信息。这显然是不正确的。正确做法应该是:这条错误信息应该放在条件判断内部,即只有当blkcnt>maxblks时才打印。另外,日志中打印的blkcnt(16,1,2,1)都小于maxblks(65536),所以不应该触发错误打印。但实际情况却打印了。因此,我们推断代码中可能存在问题:这条打印语句放错了位置,它应该放在条件判断内部。但是目前它被放在了函数开头,没有条件判断,所以每次调用该函数都会打印。另外,注意条件编译部分:如果定义了FEATURE_NONBLOCKING_RW,那么当blkcnt>maxblks时,会打印错误并触发BUG_ON。但是,由于日志中blkcnt并没有大于maxblks,所以不会触发BUG_ON,但是那条无条件打印却每次都会执行。所以,这些日志是异常的,因为它们本不应该被打印(因为blkcnt并不大于maxblks)。问题在于打印语句被错误地放置在了函数开头,而不是条件判断内部。修改建议:将这条打印语句移动到条件判断内部,并且只当blkcnt>maxblks时才打印。原代码:msdc_pr_err("[SD%d]Non-blockingRWdoesnotallowblkcnt(%d)>maxblks(%d)\n",host->id,blkcnt,maxblks);#ifdefFEATURE_NONBLOCKING_RWif(blkcnt>maxblks){msdc_pr_err("[SD%d]Non-blockingRWdoesnotallowblkcnt(%d)>maxblks(%d)\n",host->id,blkcnt,maxblks);BUG_ON(1);}#endif注意这里打印了两次?实际上,第一次在函数开头是无条件打印,第二次在条件判断内。所以每次调用都会打印一次,然后如果条件满足再打印一次并触发BUG。这显然是错误的。所以,函数开头的打印语句应该是多余的,或者是调试过程中遗留的。结论:日志异常是因为代码中存在一条多余的打印语句,该语句无论条件如何都会打印。需要删除函数开头的那条打印语句,只保留条件判断内的打印。因此,日志中的这些条目是错误打印,实际并不代表错误条件被触发(因为blkcnt<maxblks)。它们是代码中错误的打印语句导致的,应该被修复。相关问题:1.为什么在非阻塞读写中需要限制单次传输的块数量?2.如何正确实现非阻塞读写的错误检查日志打印?3.在MMC驱动中,max_phys_segs通常由什么决定?如何优化?4.块设备写入操作中,如何确保数据完整性传输效率?</think> ### 日志异常分析 #### 日志与代码逻辑对比 分析您提供的日志数据: ``` [6544] [SD0] Non-blocking RW does not allow blkcnt(16) > maxblks(65536) [7444] [SD0] Non-blocking RW does not allow blkcnt(1) > maxblks(65536) [8276] [SD0] Non-blocking RW does not allow blkcnt(2) > maxblks(65536) [9176] [SD0] Non-blocking RW does not allow blkcnt(1) > maxblks(65536) ``` 对比代码中的限制条件: ```c #ifdef FEATURE_NONBLOCKING_RW if (blkcnt > maxblks) { msdc_pr_err("[SD%d] Non-blocking RW does not allow blkcnt(%d) > maxblks(%d)\n", host->id, blkcnt, maxblks); BUG_ON(1); } #endif ``` #### 异常点诊断 1. **比较逻辑矛盾**: - 日志中所有触发条件均为:$blkcnt < maxblks$ - 例:$16 < 65536$,$1 < 65536$,$2 < 65536$ - 但代码触发条件为:$blkcnt > maxblks$ - **矛盾**:实际比较关系与日志描述完全相反 2. **BUG_ON触发异常**: - 当 $blkcnt > maxblks$ 时,`BUG_ON(1)` 会强制内核崩溃 - 日志显示$blkcnt$远小于$maxblks$,不应触发错误,但系统仍正常运行,证明: - 错误打印被错误触发 - `BUG_ON` 未被执行 3. **实际代码执行路径**: ```c // 无条件打印(问题根源) msdc_pr_err("[SD%d] Non-blocking RW does not allow blkcnt(%d) > maxblks(%d)\n", host->id, blkcnt, maxblks); // 条件检查 #ifdef FEATURE_NONBLOCKING_RW if (blkcnt > maxblks) { ... // 实际未执行 } #endif ``` - **核心问题**:日志打印语句在条件检查之外,每次调用必打印 - **误导性**:即使 $blkcnt < maxblks$ 也错误提示 $blkcnt > maxblks$ #### 问题本质 1. **代码缺陷**: - 无条件 `msdc_pr_err` 应移至条件判断内部 - 当前实现导致所有写入操作都会打印错误日志 2. **修正建议**: ```c // 修正后代码 #ifdef FEATURE_NONBLOCKING_RW if (blkcnt > maxblks) { msdc_pr_err("[SD%d] Actual error: blkcnt(%d) > maxblks(%d)\n", host->id, blkcnt, maxblks); BUG_ON(1); } #endif ``` #### 影响评估 | 日志条目 | blkcnt值 | maxblks值 | 实际是否错误 | 日志是否合理 | |---------|---------|----------|-------------|------------| | 6544 | 16 | 65536 | 否 | 异常打印 | | 7444 | 1 | 65536 | 否 | 异常打印 | | 8276 | 2 | 65536 | 否 | 异常打印 | | 9176 | 1 | 65536 | 否 | 异常打印 | **结论**:所有日志均为错误触发,系统实际正常工作,需修改代码逻辑消除误导性日志[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值