【问题标题】:How to abort a list of tasks from the outside如何从外部中止任务列表
【发布时间】:2026-02-14 18:10:01
【问题描述】:

我对 .net 4.5 中的线程模型(或一般的线程)仍然相当陌生。我正在尝试构建一组抽象类,以实现用于多线程处理一系列类似任务的框架。规则是输入的第一行告诉集合中有多少问题,接下来的行包含每个问题的输入。一组输入的多少行可能因一项任务而异,但对于给定类型的问题通常是一致的。每个问题都是以单行文本作为其输出。

我们的目标是能够从这些类继承,并且只需要担心实现 CreateWorker 和 DoWork 方法。 UI 将调用 ProcessInput 来开始工作,并调用 WriteOutput 来列出结果。

我目前的基本模板是:

public abstract class Wrapper
{
    protected int N;
    protected WorkerBase[] ProblemSet;
    protected List<Task> Tasks;

    public virtual void ProcessInput(TextReader input)
    {
        string Line = input.ReadLine();
        N = int.Parse(Line);
        ProblemSet = new WorkerBase[N];
        Tasks= new List<Task>(N);
        for (int index = 0; index < N; index++)
        {
            WorkerBase T = CreateWorker(input);
            Tasks.Add(T.DoWorkAsync());
            ProblemSet[index] = T;
        }
    }

    public virtual bool WriteOutput(TextWriter outputStream, int timeout)
    {
        bool Complete = Task.WaitAll(Tasks.ToArray(), timeout);

        for (int index = 0; index < N; index++)
        {
            outputStream.WriteLine(ProblemSet[index].Result);
        }

        return Complete;
    }

    protected abstract WorkerBase CreateWorker(TextReader inputStream);
    protected abstract class WorkerBase
    {
        public string Result
        {
            get;
            protected set;
        }
        protected abstract void DoWork();
        public Task DoWorkAsync()
        {
            return Task.Run(() => DoWork());
        }
    }
} 

事实上,这符合我的要求。但现在我想向 Wrapper 类添加中止功能,它能够停止所有线程。所以问题是,我如何在这个结构中使用中止功能,以使我的 UI 在单击取消按钮时可以中止进程?最好不必在 DoWork 实现中编写额外的代码。

我发现了传递取消令牌以中止任务的模式,但我发现的每个版本都需要 DoWork 检查令牌的状态并自行中止。我真的想要一种从方法外部中止 DoWork 的方法。

【问题讨论】:

  • 您应该将任务列表称为任务而不是线程。任务!=线程,不同的概念。对于您的问题,请查看 CancelationTokenSource msdn.microsoft.com/en-us/library/dd537607.aspx
  • 记录并重命名了该集合。我已经阅读了链接。希望有另一种模式。

标签: c# multithreading .net-4.5


【解决方案1】:

中止正在运行的代码可以通过两种方式完成(好吧,如果你包括“它永远不会从要做的事情队列中开始”,我怀疑这已经发生在取消的任务中),其中只有一种是理智的:

  • 工作定期检查一些“中止”标志并干净地终止自己
  • 你中止了整个线程

首先是你应该做什么;在大多数情况下,它工作正常。第二个几乎从来都不是一个好主意。您甚至应该考虑这一点的唯一时间是,当您的整个过程病入膏肓,您只想尽快放下它(并摆脱它的痛苦)。中止线程后,您很有可能使系统处于不可恢复的位置,因此绝不能将其用于大多数正常操作。另外:您需要对所涉及的每个 Thread 有一个引用,而这里没有。

所以:剩下第一种情况。坦率地说,我认为你应该在这里获取并检查标志。但是,如果您的工作人员需要定期从某处获取值,另一种可以很好工作的方法是让所有输入属性/方法/等进行检查那里,并且引发一个众所周知的异常,即您的工作人员在访问 foo.Count(或类似的)时可能会收到异常。

但是,通常最好正式检查取消并报告您已取消,以便可以维护任务的状态。不过,我想您也可以通过 catch 块执行此操作。

【讨论】: