簡體   English   中英

使用 semaphoreslim 取消異步任務需要很長時間

[英]Cancelling async tasks with semaphoreslim takes too long

場景是將大量的各種任務排隊,限制它們以進行並行處理,並且能夠取消它們。 我的問題是取消它們實際上比任務本身花費的時間更長,因為所有任務都已經掛在信號量中。

顯然,.Run 將所有任務扔到 .WaitAsync 中,因此具有 WaitingForActivation 狀態。 因此,賦予任務本身的令牌實際上毫無意義:所有 1000 個任務都已在運行。

將令牌提供給 WaitAsync 似乎會在取消時凍結應用程序。

static SemaphoreSlim batcher = new SemaphoreSlim(5);

        static void Main(string[] args)
        {            
            var tokenStore = new CancellationTokenSource();
            var tasks = Enumerable.Range(1, 1000).Select(i => 
                DoableWork(tokenStore.Token)
            ).ToList();

            do {
                if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape) {
                    Console.WriteLine("Cancelling tasks");
                    tokenStore.Cancel();
                }
                Thread.Sleep(1000);
                Console.WriteLine($"Tasks: {string.Join(", ", tasks.GroupBy(t => t.Status).Select(x => $"{x.Count()} {x.Key}"))}");
            } while (tasks.Any(t => t.Status < TaskStatus.RanToCompletion));
        }

        private static Task DoableWork(CancellationToken token)
        {
            return Task.Run(async () => {
                try {
                    await batcher.WaitAsync();
                    token.ThrowIfCancellationRequested();
                    await Task.Delay(200, token); // Do stuff
                } catch (OperationCanceledException) {
                    throw;
                } catch (Exception) {
                    // Logging
                    throw;
                } finally {
                    batcher.Release();
                }
            }, token);
        }

結果:

Tasks: 25 RanToCompletion, 975 WaitingForActivation
Tasks: 50 RanToCompletion, 950 WaitingForActivation
Tasks: 75 RanToCompletion, 925 WaitingForActivation
Tasks: 100 RanToCompletion, 900 WaitingForActivation
Tasks: 120 RanToCompletion, 880 WaitingForActivation
Tasks: 145 RanToCompletion, 855 WaitingForActivation
Cancelling tasks
Tasks: 145 RanToCompletion, 16 Canceled, 839 WaitingForActivation
Tasks: 145 RanToCompletion, 33 Canceled, 822 WaitingForActivation
Tasks: 145 RanToCompletion, 51 Canceled, 804 WaitingForActivation
Tasks: 145 RanToCompletion, 66 Canceled, 789 WaitingForActivation
Tasks: 145 RanToCompletion, 81 Canceled, 774 WaitingForActivation
Tasks: 145 RanToCompletion, 101 Canceled, 754 WaitingForActivation

如您所見,每秒取消的任務少於處理的任務。 按照這個速度,您需要等待一分鍾才能取消 1000 個待處理的任務。

使用 new Task() 進行替代,並調用 Task.Start() 將導致中斷取消機制,而不是從每個正在運行的任務中拋出未處理的異常。

tasks.Where(t => t.Status < TaskStatus.Running)
     .Take(5 - tasks.Count(t => t.Status == TaskStatus.Running))
     .ToList()
     .ForEach(t => t.Start());

private static Task DoableWork(CancellationToken token)
        {
            return new Task(async () => {
                try {
                    await Task.Delay(200, token); // Do stuff
                } catch (OperationCanceledException) {
                    throw;
                } catch (Exception) {
                    // Logging
                    throw;
                }
            }, token);
        }

發現問題。 VS 2019(或其設置之一)在調試模式下顯着減慢了異常處理速度。 當我直接運行 .exe 時,取消是即時的。 感謝@TheodorZoulias 顯示未能重現該問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM