如何实现按下'c'后让Parallel.ForEach在已启动任务完成后停止
实现Parallel.ForEach按下'c'停止新任务但完成已启动任务
你现在的代码问题在于把按键监听放在了每个并行任务的逻辑里,这会导致每个任务都阻塞等待用户输入,完全偏离了“随时按下'c'就停止新任务”的需求。我来给你调整成正确的实现方式:
核心思路
我们需要单独开一个线程监听全局按键输入,而不是在并行任务里处理;同时利用Parallel.ForEach的state.Break()方法来停止调度新任务,确保已启动的任务能正常完成。
完整代码实现
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; class Program { static void Main() { List<int> companies = new List<int> { 56, 1, 18, 26, 37, 45, 47, 50, 56, 60 }; var cancellationTokenSource = new CancellationTokenSource(); // 启动独立线程监听用户按键 _ = Task.Run(() => { while (true) { // 使用true参数避免按键显示在控制台 var key = Console.ReadKey(true); if (key.KeyChar == 'c') { cancellationTokenSource.Cancel(); Console.WriteLine("\n已收到停止信号,不再启动新任务..."); break; } } }); try { Parallel.ForEach( companies, new ParallelOptions { CancellationToken = cancellationTokenSource.Token }, (company, state) => { // 检查是否收到停止信号,若收到则停止新任务调度 if (cancellationTokenSource.Token.IsCancellationRequested) { state.Break(); return; } // 执行你的业务逻辑 MyFunction(company); } ); } catch (OperationCanceledException) { // 捕获Parallel.ForEach的取消异常,这里可以做一些收尾提示 Console.WriteLine("等待所有已启动的任务完成..."); } Console.WriteLine("所有任务处理完成,程序即将退出。"); } static void MyFunction(int company) { // 模拟业务逻辑的执行时间,替换成你的实际代码 Thread.Sleep(1000); Console.WriteLine($"处理公司ID: {company} 完成"); } }
关键细节解释
- 独立按键监听线程:用
Task.Run启动一个后台线程专门监听按键,这样不会阻塞并行任务的执行,用户随时按下'c'都能触发停止信号。 - CancellationTokenSource:用来传递停止信号,既可以通知
Parallel.ForEach,也能让我们在每个迭代里检查是否需要停止新任务。 - state.Break():这个方法是
Parallel类的关键——它会告诉框架停止调度新的迭代任务,但已经开始执行的迭代会继续运行直到完成,完全符合你的需求。 - 异常处理:当
CancellationToken被取消时,Parallel.ForEach会抛出OperationCanceledException,我们捕获它来做收尾提示,不影响已启动任务的执行。
这样修改后,你的程序就能实现:用户随时按下'c',立刻停止启动新任务,等所有正在运行的任务做完后自动终止。
内容的提问来源于stack exchange,提问作者Aleksander Chelpski
相关产品推荐
相关产品推荐

