首页 > 解决方案 > 在 CancellationTokenSource 上使用 CancelAfter 和循环

问题描述

我想创建一个实现超时的类助手。因为任务一旦启动就无法停止,我使用循环。我想在不通过 GetAsync 的 CancellationToken 的情况下使用此模式。我的问题是:这个代码是你可以使用的最好的,还是循环会导致性能问题?要了解示例:如果您更改https://reqres.in/api/users?delay=3(延迟=4),您没有超时。如果没有提出 CancelAfter 的另一个重要问题,为什么我的代码仍然可以运行(见输出)?

using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace RetryDemo {
    class Program {
        static void Main(string[] args) {
            Example();
            Console.ReadLine();
        }
        private static void Example() {
            Stopwatch Stopwatch = Stopwatch.StartNew();
            try {
                AppDomain.CurrentDomain.UnhandledException += (sender, e) => { Console.WriteLine(((AggregateException)e.ExceptionObject).InnerExceptions.Single().Message); };
                var source = new CancellationTokenSource();
                var token = source.Token;

                var task = Task.Run(async () => {
                    try {
                        using (token.Register(() => {
                            Console.WriteLine($"Timeout elapsed {Stopwatch.Elapsed}");
                        })) {
                            Task<HttpResponseMessage> message = GetPage("https://reqres.in/api/users?delay=4");
                            while (true) {
                                if (!token.IsCancellationRequested) {
                                    if (message.IsCompleted) {
                                        HttpResponseMessage messageSync = await message;
                                        Console.WriteLine(await messageSync.Content.ReadAsStringAsync());
                                    }
                                } else {
                                    break;
                                }
                                Thread.Sleep(200);
                                Console.Write(".");
                            }
                        }
                    } catch (Exception ex) {
                        Console.WriteLine(ex.Message);
                    }
                });

                source.CancelAfter(3500);


            } catch (Exception ex) {
                Console.Write(ex.Message);
            }
        }
        public static async Task<HttpResponseMessage> GetPage(string url) {
            Console.WriteLine($"Calling page{url}");
            HttpClient httpclient = new HttpClient();
            return await httpclient.GetAsync(url);//.ConfigureAwait(false);
        }
    }
}

标签: c#cancellationtokensource

解决方案


我想在不通过 GetAsync 的 CancellationToken 的情况下使用此模式。

请记住,取消是合作的,所以这最终只会取消等待而不是操作本身。

它是您可以使用的最好的还是循环会导致性能问题?

一个完全正确的解决方案相当棘手,因为很容易忘记在正确的时间处理所有东西。我建议TaskExtensions.WaitAsync(CancellationToken)从我的AsyncEx 库中使用。它不使用轮询;相反,它用于Register在超时时立即取消等待。


推荐阅读