c# Task任务的取消

Task任务的取消

c# 任务的取消,需要用到CancellationTokenSource类,CancellationToken结构体。
注意:CancellationTokenSource是class类型,而CancellationToken是struct结构体。
任务内部"监听"CancellationToken方法:
任务的内部在合适的时候不停地调用CancellationToken的ThrowIfCancellationRequested()方法,这个函数会抛出一个叫做OperationCanceledException的异常,它的实现(微软开源代码)如下:

        public void ThrowIfCancellationRequested()
        {
            if (IsCancellationRequested) 
                ThrowOperationCanceledException();
        }

下面的列子通过Task.Run产生了一个任务。Task任务的内部正式通过抛出OperationCanceledException异常达到被取消的目的。而任务的外部则是通过调用可以通过CancellationTokenSource实例的Cancel()方法来触发取消的动作的。
下面的例子是一个取消Task任务的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;
        var tokenSource3 = new CancellationTokenSource();

        var task = Task.Run(() =>
        {
            // Were we already canceled?
            ct.ThrowIfCancellationRequested();
            
            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                // Clean up here, then...
                if(ct.IsCancellationRequested)
                {
                    ct.ThrowIfCancellationRequested();
                }
            }
        },tokenSource2.Token); // Pass same token to Task.Run.

        Thread.Sleep(5000);
        tokenSource2.Cancel();

        // Just continue on this thread, or await with try-catch:
        try
        {
            await task;
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
            bool eqs = e.CancellationToken.Equals(tokenSource3.Token);
            System.Console.WriteLine($"Equals' result: {eqs}.");
            System.Console.WriteLine($"Task's status: {task.Status}");
        }
        catch (Exception ex)
        {
            System.Console.WriteLine(ex.Message);
        }
        finally
        {
            tokenSource2.Dispose();
        }

        Console.ReadKey();
    }
}
/*
The operation was canceled.
OperationCanceledException thrown with message: The operation was canceled.
Equals' result: False.
Task's status: Canceled
*/

这个例子中我们在Task的外部创建了2个CancellationTokenSource实例,因为CancellationTokenSource类包含一个CancellationToken类的属性Token,这也就意味着例子当中包含了两个CancellationToken实例。ThrowIfCancellationRequested()方法会把调用它的CancellationToken实例也作为参数一起携带抛出。因此catch块参数e中包含的是tokenSource2.Token,因此它与tokenSource3.Token进行相等比较输出一定是false。又因为Task.Run()方法中传入的第二个参数也是tokenSource2.Token,Task核心框架内部会比较OperationCanceledException异常中携带的CancellationToken实例和Task.Run()方法中传入的CancellationToken实例,如果两者是一样的,则Task的status状态设为Canceled,表示成功取消;如果两者不一样,Task任务仍然会退出,但是Task的status状态设为Faulted,表示出错。
如果将第29行改为:

        },tokenSource3.Token); // Pass same token to Task.Run.

则,结果输出如下:

OperationCanceledException thrown with message: The operation was canceled.
Equals' result: False.
Task's status: Faulted

看起来,Task任务也被取消了,但是实际上是由于出错而退出。因此,Task.Run的第二个参数可以用来确保内部实际引发取消异常的CancellationToken实例和Task.Run传入的CancellationToken实例要一致才能成功取消,否则框架会误认为你是误操作而导致退出的。因此,Task.Run的CancellationToken参数是为了更加安全。
如果将29行改为不带CancellationToken参数的重载函数,那么返回的也是一样的Faulted结果。

发布了108 篇原创文章 · 获赞 126 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_16587307/article/details/103936727