在 CancellationTokenSource 上使用 CancelAfter 和循环

Posted

技术标签:

【中文标题】在 CancellationTokenSource 上使用 CancelAfter 和循环【英文标题】:Use CancelAfter on CancellationTokenSource with loop 【发布时间】:2021-06-17 13:05:33 【问题描述】:

我想创建一个实现超时的类助手。 因为任务一旦启动就无法停止,我使用循环。 我想在不通过 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 pageurl");
            HttpClient httpclient = new HttpClient();
            return await httpclient.GetAsync(url);//.ConfigureAwait(false);
        
    

【问题讨论】:

“我想使用这个模式而不在 GetAsync 上传递 CancellationToken。” 请问为什么? GetAsync 是一个使用示例,我的意图是使用一个执行时间最长的辅助任务 什么辅助任务?您将如何实现最大执行时间?请尽量完整地回答您的问题,以便我们为您提供最好的帮助 【参考方案1】:

我想在 GetAsync 上不通过 CancellationToken 使用此模式。

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

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

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

【讨论】:

以上是关于在 CancellationTokenSource 上使用 CancelAfter 和循环的主要内容,如果未能解决你的问题,请参考以下文章

对 CancellationTokenSource.Cancel 的调用永远不会返回

在 CancellationTokenSource 上使用 CancelAfter 和循环

异步与并行~CancellationTokenSource对线程的作用

在取消和处理 CancellationTokenSource 之间存在延迟是不是可以保证 IsCancellationRequested 将设置为 true?

C# 中通过CancellationTokenSource实现对超时任务的取消

浅谈C#取消令牌CancellationTokenSource