• 取消任务


    >>返回《C# 并发编程》

    CancellationToken.None 是一个等同于默认的特殊值,表示这个方法是永远不会被取消的。

    实例代码

    static async Task CancelableMethodAsync(CancellationToken token)
    {
        await Task.Delay(1000, token);
        throw new ArgumentException();
    }
    public static async Task IssueCancelRequestAsync()
    {
        var cts = new CancellationTokenSource();
        var task = CancelableMethodAsync(cts.Token);
        // 这里,操作在正常运行。
        // 发出取消请求。
        cts.Cancel();
        //(异步地)等待操作结束。
        try
        {
            await task;
            // 如运行到这里,说明在取消请求生效前,操作正常完成 。
        }
        catch (OperationCanceledException ex)
        {
            // 如运行到这里,说明操作在完成前被取消。
            System.Console.WriteLine(ex.GetType().Name);
        }
        catch (Exception ex)
        {
            // 如运行到这里,说明在取消请求生效前,操作出错并结束。
            System.Console.WriteLine(ex.GetType().Name);
        }
    }
    

    输出:

    TaskCanceledException
    

    1. 取消请求

    public static int CancelableMethod(CancellationToken cancellationToken)
    {
        for (int i = 0; i != 100000; ++i)
        {
            // cancellationToken.WaitHandle.WaitOne(1000);
            Thread.Sleep(1);
            // 这里做一些计算工作。
            if (i % 1000 == 0)
                cancellationToken.ThrowIfCancellationRequested();
        }
        return 42;
    }
    

    2. 超时后取消

    public static async Task IssueTimeoutAsync()
    {
        Stopwatch sw = Stopwatch.StartNew();
        try
        {
            var cts = new CancellationTokenSource();
            var token = cts.Token;
            cts.CancelAfter(TimeSpan.FromSeconds(2));
            await Task.Delay(TimeSpan.FromSeconds(4), token);
        }
        finally
        {
            System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
        }
    }
    

    输出:

    2004ms
    Unhandled Exception: ... ...
    

    只要执行代码时用到了超时,就该使用 CancellationTokenSourceCancelAfter (或者用构造函数)。虽然还有其他途径可实现这个功能,但是使用现有的取消体系是最简单也是最高效的。

    3. 取消并行

    public class Matrix
    {
        public void Rotate(float degrees) { }
    }
    
    //只做展示
    public static void RotateMatrices(IEnumerable<Matrix> matrices, float degrees, CancellationToken token)
    {
        Parallel.ForEach(matrices, new ParallelOptions
        {
            CancellationToken = token
        },
        matrix => matrix.Rotate(degrees));
    }
    

    4. 取消响应式代码

    注入取消请求

    • 某一个层次的代码需要响应取消请求,同时它本身也要向下一层代码发出取消请求(但不会向上传递)。
    public static async Task RunGetWithTimeoutAsync()
    {
        CancellationTokenSource source = new CancellationTokenSource();
        await GetWithTimeoutAsync("http://www.baidu.com", source.Token);
    }
    
    public static async Task<HttpResponseMessage> GetWithTimeoutAsync(string url, CancellationToken cancellationToken)
    {
        var client = new HttpClient();
        using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
        {
            cts.CancelAfter(TimeSpan.FromMilliseconds(100));
            var combinedToken = cts.Token;
            return await client.GetAsync(url, combinedToken);
        }
    }
    

    输出:

    Unhandled Exception: Unhandled exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AggregateException: One or more errors occurred. (A task was canceled.) ---> System.Threading.Tasks.TaskCanceledException ... ...

    5. 与其他取消体系的互操作

    有一些外部的或以前遗留下来的代码采用了非标准的取消模式。现在要用标准的CancellationToken 来控制这些代码

    public static async Task RunPingAsync()
    {
        var cts = new CancellationTokenSource();
        var task = PingAsync("192.168.0.101", cts.Token);
        //cts.Cancel();
        await task;
    }
    public static async Task<PingReply> PingAsync(string hostNameOrAddress, CancellationToken cancellationToken)
    {
        Stopwatch sw = Stopwatch.StartNew();
        try
        {
            var ping = new Ping();
            using (cancellationToken.Register(() => ping.SendAsyncCancel()))
            {
                return await ping.SendPingAsync(hostNameOrAddress);
            }
        }
        finally
        {
            System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
        }
    }
    

    注意: 为了避免内存和资源的泄漏,一旦不再需要使用回调函数了,就要释放这个回调函数注册。

  • 相关阅读:
    [搜片神器]单服务器程序+数据库流程优化记录
    [搜片神器]直接从DHT网络下载BT种子的方法
    [搜片神器]winform程序自己如何更新自己的方法代码
    实时排行榜的后台数据功能实现
    我的Java之旅 第八课 Servlet 进阶API、过滤器与监听器
    我的Java之旅 第七课 JAVA WEB 会话管理
    我的Java之旅 第六课 JAVA WEB 请求与响应
    我的Java之旅 第五课 JAVA 语言语法 集合
    我的Java之旅 第四课 JAVA 语言语法 基础
    我的Java之旅 第三课 从Applet到JSP
  • 原文地址:https://www.cnblogs.com/BigBrotherStone/p/12248808.html
Copyright © 2020-2023  润新知