如何同时使用ThreadPool与Semaphore实现递归递减打印功能?
使用ThreadPool和SemaphoreSlim实现递归递减打印
问题分析
你之前的ThreadPool版本代码出现终止值重复打印的问题,根源在于递归逻辑的执行时机和信号量控制不匹配,加上分支重复代码导致的逻辑冗余。原代码中,每次递归调用通过线程池异步执行,但信号量释放和递归入队的顺序可能触发多次终止条件,同时if分支的独立逻辑也容易引发重复执行。
修正后的实现方案
我们需要统一递归逻辑,用信号量正确保护控制台输出和递归流程,避免异步执行带来的参数捕获和并发问题。以下是修正后的完整代码:
核心思路
- 用
SemaphoreSlim创建单许可信号量,保证同一时间只有一个线程进入打印和递归入队的临界区。 - 统一打印逻辑,减少分支重复代码,避免原代码中else分支打印
count-1、if分支打印1的不一致问题。 - 提前捕获递归参数,避免异步闭包导致的参数值异常。
完整代码
// 静态单许可信号量,保证串行执行临界区代码 private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private static void RecursionWithThreadPool(int count) { _semaphore.Wait(); try { // 统一打印当前数值,避免分支逻辑差异 Console.WriteLine("ThreadPool Current value is {0}", count); // 递归终止条件:count递减到1时停止 if (count > 1) { // 提前赋值避免闭包捕获变化的count值 int nextCount = count - 1; ThreadPool.QueueUserWorkItem(_ => RecursionWithThreadPool(nextCount)); } } finally { // 确保信号量始终被释放,避免死锁 _semaphore.Release(); } }
代码说明
- 信号量控制:
SemaphoreSlim(1,1)限制同一时间只有一个线程执行打印和递归入队操作,既保证控制台输出有序,也避免递归逻辑并发执行导致的混乱。 - 统一逻辑:无论是否是终止条件,都直接打印当前
count值,递归时传递count-1,彻底解决原代码中两次打印1的问题。 - 闭包参数处理:提前将
count-1赋值给nextCount,避免线程池异步执行时捕获到后续变化的count变量,确保递归参数准确。 - finally释放:在finally块中释放信号量,无论代码是否抛出异常,都能保证信号量不会被永久占用。
调用示例
// 从5开始递归递减打印 RecursionWithThreadPool(5); // 等待线程池任务执行完成(生产环境可使用CountdownEvent等更可靠的等待方式) Thread.Sleep(1000);
输出结果:
ThreadPool Current value is 5 ThreadPool Current value is 4 ThreadPool Current value is 3 ThreadPool Current value is 2 ThreadPool Current value is 1
原代码错误原因回顾
你遇到的重复打印1的问题,本质是原代码逻辑的不一致:
- 当
count=2时,else分支打印count-1=1,同时入队count-1=1的递归调用; - 线程池执行该递归调用时,if分支又会打印一次1,最终导致两次输出。
修正后的代码统一打印当前count值,递归传递count-1,从根源避免了重复打印。
内容的提问来源于stack exchange,提问作者Вася Пупкин
相关产品推荐
相关产品推荐

