是否有取消异步任务的正确方法? [英] Is there a proper way to cancel an asynchronous task?

查看:146
本文介绍了是否有取消异步任务的正确方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个如何正确取消异步任务的问题。

I encountered a problem how to properly cancel an asynchronous task.

这里是一些草稿。

我的入口点运行两个异步任务。第一个任务完成了一些长时间工作,第二个任务取消了它。

My entry point runs two asynchronous task. The first task does some 'long' work and the second one cancels it.

入口点:

private static void Main()
{
    var ctc = new CancellationTokenSource();

    var cancellable = ExecuteLongCancellableMethod(ctc.Token);

    var cancelationTask = Task.Run(() =>
    {
        Thread.Sleep(2000);

        Console.WriteLine("[Before cancellation]");

        ctc.Cancel();
    });

    try
    {
        Task.WaitAll(cancellable, cancelationTask);
    }
    catch (Exception e)
    {
        Console.WriteLine($"An exception occurred with type {e.GetType().Name}");
    }
}

返回可取消任务的方法:

private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
    return Task.Run(() =>
    {
        token.ThrowIfCancellationRequested();

        Console.WriteLine("1st"); 
        Thread.Sleep(1000);

        Console.WriteLine("2nd");
        Thread.Sleep(1000);

        Console.WriteLine("3rd");
        Thread.Sleep(1000);

        Console.WriteLine("4th");
        Thread.Sleep(1000);

        Console.WriteLine("[Completed]");

    }, token);  
}   

我的目的是停止写 1st, 2nd, 3rd取消后立即被调用。 但是我得到以下结果:

My purpose is to stop writing '1st','2nd','3rd' immediately after cancellation is called. But I get following results:

1st
2nd
3rd
[Before cancellation]
4th
[Completed]

出于明显的原因请求取消时,我没有抛出异常。因此,我尝试将方法重写如下:

For obvious reason I didn't get an exception that throws when cancellation is requested. So I tried to rewrite method as following:

private static Task ExecuteLongCancellableAdvancedMethod(CancellationToken token)
{
    return Task.Run(() =>
    {
        var actions = new List<Action>
        {
            () => Console.WriteLine("1st"),
            () => Console.WriteLine("2nd"),
            () => Console.WriteLine("3rd"),
            () => Console.WriteLine("4th"),
            () => Console.WriteLine("[Completed]")
        };

        foreach (var action in actions)
        {
            token.ThrowIfCancellationRequested();

            action.Invoke();

            Thread.Sleep(1000);
        }

    }, token);
}

现在我得到了想要的东西:

And now I got what I want:

1st
2nd
[Before cancellation]
3rd
An exception occurred with type AggregateException

但是我想创建一个Action委托集合并遍历它并不是解决我的问题的最方便方法。

But I guess creating of a collection of Action delegates and looping through it is not the most convenient way to deal with my problem.

那么正确的方法是什么?为什么我需要将取消令牌作为第二个参数传递给Task.Run方法?

推荐答案

任务不会取消其自身,您有责任检测取消请求并彻底中止工作。这就是 token.ThrowIfCancellationRequested(); 的作用。

The Task won't cancel its self, it is up you you to detect the cancellation request and cleanly abort your work. That's what token.ThrowIfCancellationRequested(); does.

您应该将这些检查放在整个代码中,放在可以完全停止执行,也可以将执行回滚到安全状态。

You should place those checks throughout your code, in places where execution can be cleanly stopped, or rolled back to a safe state.

在第二个示例中,您在循环的每次迭代中都调用它一次,并且效果很好。第一个示例从一开始就只调用一次。如果到那时还没有取消令牌,则任务将完成,就像您看到的一样。

In your second example, you call it once per iteration of the loop, and it works fine. The first example only calls it once, at the very start. If the token hasn't been canceled by that point, the task will run to completion, just like you are seeing.

如果将其更改为如下所示,您将还会看到您期望的结果。

If you changed it to look like this, you would also see the results you expect.

return Task.Run(() =>
{
    token.ThrowIfCancellationRequested();
    Console.WriteLine("1st"); 
    Thread.Sleep(1000);

    token.ThrowIfCancellationRequested();
    Console.WriteLine("2nd");
    Thread.Sleep(1000);

    token.ThrowIfCancellationRequested();
    Console.WriteLine("3rd");
    Thread.Sleep(1000);

    token.ThrowIfCancellationRequested();
    Console.WriteLine("4th");
    Thread.Sleep(1000);

    Console.WriteLine("[Completed]");

}, token); 

这篇关于是否有取消异步任务的正确方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆