在C#中取消多个任务的正确方法是什么?

31
我有一个按钮,它会生成4个任务。同样的按钮会变成取消按钮,点击后应该取消所有4个任务。我应该将同一取消标记传递给所有4个任务,并让它们在同一标记上轮询IsCancelRequested吗?在阅读createlinkedtokensource的MSDN文档后,我感到困惑。通常是如何完成这个过程的?谢谢
更新:Task.WaitAll()会等待所有任务执行完毕。同样,当共享取消令牌源被设置为取消时,如何知道所有任务都已被取消?

我更新了答案以反映您的更新。 - Matt
3个回答

32

是的,你所说的使用单个CancellationToken是正确的。你可以创建一个单独的CancellationTokenSource并使用它的CancellationToken来处理所有任务。你的任务应该定期检查令牌是否已取消。

例如:

const int NUM_TASKS = 4;

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;

Task[] tasks = new Task[NUM_TASKS];
for (int i = 0; i < NUM_TASKS; i++)
{
    tasks[i] = Task.Factory.StartNew(() =>
    {
        while (true)
        {
            Thread.Sleep(1000);
            if (ct.IsCancellationRequested)
                break;
        }
    }, ct);
}

Task.WaitAll(tasks);

你的按钮可以调用 cts.Cancel(); 来取消任务。

问题更新的说明:

有几种方法可以实现你所要求的功能。其中一种方法是使用 ct.IsCancellationRequested 来检查是否已经取消,而不是直接抛出异常,然后允许你的任务完成。此时,Task.WaitAll(tasks) 将在所有任务都被取消后完成。

我已经更新了代码以反映这个变化。


这将最多阻塞UI 4秒钟。 - Alan Turing
1
这是一个示例,它将等待所有任务完成,每个任务每秒钟检查一次完成情况。它没有给出完整的GUI应用程序上下文。 - Matt
我同意@Matt的答案。但是我想指出,var task = Task.WhenAll(tasks); 更容易管理。如果任何任务失败或被取消,WaitAll() 将抛出一个异常,而且是一个丑陋的 AggregateException。请参阅此方法在 MSDN 上定义的行为。在绝大多数情况下,这种行为意味着你只需要检查返回的任务(来自 WhenAll())状态即可。 - Jonno
@Matt,感谢您的解决方案。我已经尝试了5个或更多任务,但它没有起作用。请问在Task.WaitAll之前我可以调用cts.Cancel()吗? - Sudhir.net

3

如果你的意图是取消所有任务,那么你应该传递相同的令牌并使用它。


-3
使用BackroundWorker类,设置属性WorkerSupportsCancellation,通过调用RunWorkerAsync()开始任务,并使用CancelAsync()停止它们。
您不需要将代码与UI同步。

开发一个BackgroundWorker不是解决方案。你可以在所有任务中使用CancellationTokenSource.Token,只需取消该源即可将它们全部取消。 - MarchalPT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接