如何取消任务而等待任务完成?

时间:2021-07-13 04:23:55

I have threaded task wich performs some operation in loop:

我在循环中执行了线程任务wich执行了一些操作:

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");
}

I start it and then cancel after some period of time:

我先开始,然后在一段时间后取消:

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

This operation must be finished correctly, I don't want to interrupt it. But I want to send a cancellation request to my task and wait until it finishes correctly (by which I mean it gets to some point in code). I tryed following approaches:

这个操作必须正确完成,我不想打断它。但是我想向我的任务发送一个取消请求,并等待它正确地完成(我的意思是它在代码中到达某个点)。如何提问以下方法:

1. Wait(CancellationToken ct)

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

In this case program returns immediately from Wait(). The task continues to run until ThrowIfCancellationRequested() but if main thread exits the task gets interrupted too.

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

2. Wait()

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

Here main thread waits for completion but at the end AggregateException is risen with InnerException = TaskCancelledException (not OperationCancelledException).

在这里,主线程等待完成,但在最终的AggregateException中,包含了InnerException = TaskCancelledException (not OperationCancelledException)。

3. Check IsCancellationRequested() and no exceptions

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

In this case no exceptions are risen but the task gets status RanToCompletion in the end. This is not distiguishable from correct completion when SomeCodition() starts to return false.

在这种情况下,没有出现异常,但任务最终获得状态RanToCompletion。当SomeCodition()开始返回false时,不能从正确的完成中提取。

All these problem have easy workarounds but I wonder, may be I'm missing something? Could anybody advise me better solution?

所有这些问题都有简单的解决办法,但我想知道,我是不是漏掉了什么?有人能给我更好的解决办法吗?

2 个解决方案

#1


5  

If you want to wait for the task to complete (or gets cancelled) synchronously, you can try this:

如果您希望等待任务同步完成(或被取消),您可以尝试以下方法:

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

So that you can directly catch OperationCanceledException instead of catching an AggregateException.

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

Edit:

编辑:

Wait(CanecllationToken)

等待(CanecllationToken)

This approach won't work for that purpose. MSDN statement:

这种方法不能达到这个目的。MSDN声明:

Waits for the Task to complete execution. The wait terminates if a cancellation token is canceled before the task completes.

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

Wait()

Wait()

You can use this approach but as you can see, you should expect an AggregateException not OperationCanceledException. It is also specified in documents of the method.

您可以使用这种方法,但是正如您所看到的,您应该期望使用AggregateException而不是OperationCanceledException。在方法的文档中也指定了它。

The AggregateException.InnerExceptions collection contains a TaskCanceledException object.

AggregateException。innerexception集合包含TaskCanceledException对象。

So in this approach, in order to make sure operation is cancelled, you can check if inner expection contains a TaskCanceledException or not.

因此,在这种方法中,为了确保操作被取消,您可以检查内部表达式是否包含TaskCanceledException。

Check IsCancellationRequested() and no exceptions

检查取消请求(),没有异常

In this way, this is obvious that no exception is thrown and you can't find out if the operation is cancelled or not.

通过这种方式,显然不会抛出异常,您无法确定操作是否被取消。

If you don't want to wait synchronously, everything works as expected:

如果你不想同步等待,一切都按预期运行:

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

#2


0  

Try this:

试试这个:

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

You'll get an OperationCanceledException and it won't be wrapped with AggregateException.

您将得到一个OperationCanceledException,它不会被AggregateException包装。

#1


5  

If you want to wait for the task to complete (or gets cancelled) synchronously, you can try this:

如果您希望等待任务同步完成(或被取消),您可以尝试以下方法:

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

So that you can directly catch OperationCanceledException instead of catching an AggregateException.

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

Edit:

编辑:

Wait(CanecllationToken)

等待(CanecllationToken)

This approach won't work for that purpose. MSDN statement:

这种方法不能达到这个目的。MSDN声明:

Waits for the Task to complete execution. The wait terminates if a cancellation token is canceled before the task completes.

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

Wait()

Wait()

You can use this approach but as you can see, you should expect an AggregateException not OperationCanceledException. It is also specified in documents of the method.

您可以使用这种方法,但是正如您所看到的,您应该期望使用AggregateException而不是OperationCanceledException。在方法的文档中也指定了它。

The AggregateException.InnerExceptions collection contains a TaskCanceledException object.

AggregateException。innerexception集合包含TaskCanceledException对象。

So in this approach, in order to make sure operation is cancelled, you can check if inner expection contains a TaskCanceledException or not.

因此,在这种方法中,为了确保操作被取消,您可以检查内部表达式是否包含TaskCanceledException。

Check IsCancellationRequested() and no exceptions

检查取消请求(),没有异常

In this way, this is obvious that no exception is thrown and you can't find out if the operation is cancelled or not.

通过这种方式,显然不会抛出异常,您无法确定操作是否被取消。

If you don't want to wait synchronously, everything works as expected:

如果你不想同步等待,一切都按预期运行:

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

#2


0  

Try this:

试试这个:

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

You'll get an OperationCanceledException and it won't be wrapped with AggregateException.

您将得到一个OperationCanceledException,它不会被AggregateException包装。