You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现按下'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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:07:13