首页 文章

如何取消任务但等到它完成?

提问于
浏览
5

我有线程任务,它在循环中执行一些操作:

static void TaskAction(CancellationToken ct)
{
    while (SomeCondition())
    {
        DoSomeSeriousJob();
        ct.ThrowIfCancellationRequested();
    }
}

static void DoSomeSeriousJob()
{
    Console.WriteLine("Serious job started");
    Thread.Sleep(5000);
    Console.WriteLine("Serious job done");
}

我启动它然后在一段时间后取消:

var cts = new CancellationTokenSource();
    var task = Task.Factory.StartNew(() => TaskAction(cts.Token), cts.Token);
    Thread.Sleep(1000);
    cts.Cancel();

这个操作必须正确完成,我不想打断它 . 但我想正确地向我的任务 and wait until it finishes 发送取消请求(我的意思是它在代码中得到了某些点) . 我尝试了以下方法:

1.等待(CancellationToken ct)

try
{
    task.Wait(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task cancelled");
}
// Must be joined here.

在这种情况下,程序立即从 Wait() 返回 . 任务继续运行,直到 ThrowIfCancellationRequested() 但是如果主线程退出,任务也会被中断 .

2.等待()

try
{
    task.Wait();
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task cancelled");
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

这里主线程等待完成,但最后 AggregateException 上升 InnerException = TaskCancelledException (不是 OperationCancelledException ) .

3.检查IsCancellationRequested()并且没有异常

static void TaskAction(CancellationToken ct)
{
    while (SomeCondition())
    {
        DoSomeSeriousJob();
        if (ct.IsCancellationRequested)
            break;
    }
}
// ...
task.Wait();

在这种情况下,没有异常上升,但任务最终获得状态 RanToCompletion . 当 SomeCodition() 开始返回false时,这不能正确完成 .

所有这些问题都有简单的解决方法,但我想知道,可能是我错过了什么?谁有人建议我更好的解决方案?

2 回答

  • 0

    如果您想等待任务同步完成(或取消),您可以尝试这样做:

    cts.Cancel();
    Task.Run(async () => {
        try {
            await task;
        }
        catch (OperationCanceledException ex) {
          // ...
        }
    ).Wait();
    

    这样您就可以直接捕获OperationCanceledException而不是捕获AggregateException .

    Edit:

    Wait(CanecllationToken)

    这种方法不适用于此目的 . MSDN声明:

    等待任务完成执行 . 如果在任务完成之前取消取消令牌,则等待终止 .

    Wait()

    您可以使用此方法,但正如您所看到的,您应该期望AggregateException而不是OperationCanceledException . 它也在方法的documents中指定 .

    AggregateException.InnerExceptions集合包含TaskCanceledException对象 .

    因此,在这种方法中,为了确保取消操作,您可以检查内部预期是否包含TaskCanceledException .

    Check IsCancellationRequested() and no exceptions

    通过这种方式,很明显没有抛出任何异常,也无法确定操作是否被取消 .

    如果您不想同步等待,一切都按预期工作:

    cts.Cancel();
    try {
        await task;
    }
    catch (OperationCanceledException ex) {
       // ...
    }
    
  • 5

    试试这个:

    try
    {
        task.GetAwaiter().GetResult();
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Task cancelled");
    }
    

    你会得到一个 OperationCanceledException ,它不会被 AggregateException 包裹 .

相关问题