Warum wird die Aufgabe nicht abgebrochen, wenn ich die Cancel-Methode von CancellationTokenSource als asynchrone Methode aufrufe?

Ich habe einen kleinen Wrapper um @ erstellCancellationToken undCancellationTokenSource. Das Problem, das ich habe, ist, dass dasCancelAsync Methode vonCancellationHelper funktioniert nicht wie erwartet.

Ich habe das Problem mit demItShouldThrowAExceptionButStallsInstead Methode. Um die laufende Aufgabe abzubrechen, ruft esawait coordinator.CancelAsync();, aber die Aufgabe wird nicht abgebrochen und wirft keine Ausnahme auftask.Wait

ItWorksWellAndThrowsException scheint gut zu funktionieren und verwendetcoordinator.Cancel, das ist überhaupt keine asynchrone Methode.

Die Frage, warum die Aufgabe nicht abgebrochen wird, wenn ich @ anruCancellationTokenSource 's Cancel-Methode in der asynchronen Methode?

Lass das @ nicwaitHandle verwirren Sie, es ist nur dafür, dass die Aufgabe nicht vorzeitig beendet wird.

Lassen Sie den Code für sich selbst sprechen:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace TestCancellation
{
    class Program
    {
        static void Main(string[] args)
        {
            ItWorksWellAndThrowsException();
            //ItShouldThrowAExceptionButStallsInstead();
        }

        private static void ItShouldThrowAExceptionButStallsInstead()
        {
            Task.Run(async () =>
            {
                var coordinator = new CancellationHelper();
                var waitHandle = new ManualResetEvent(false);

                var task = Task.Run(() =>
                {
                    waitHandle.WaitOne();

                    //this works well though - it throws
                    //coordinator.ThrowIfCancellationRequested();

                }, coordinator.Token);

                await coordinator.CancelAsync();
                //waitHandle.Set(); -- with or without this it will throw
                task.Wait();
            }).Wait();
        }

        private static void ItWorksWellAndThrowsException()
        {
            Task.Run(() =>
            {
                var coordinator = new CancellationHelper();
                var waitHandle = new ManualResetEvent(false);

                var task = Task.Run(() => { waitHandle.WaitOne(); },, coordinator.Token);

                coordinator.Cancel();
                task.Wait();
            }).Wait();
        }
    }

    public class CancellationHelper
    {
        private CancellationTokenSource cancellationTokenSource;
        private readonly List<Task> tasksToAwait;

        public CancellationHelper()
        {
            cancellationTokenSource = new CancellationTokenSource();
            tasksToAwait = new List<Task>();
        }

        public CancellationToken Token
        {
            get { return cancellationTokenSource.Token; }
        }

        public void AwaitOnCancellation(Task task)
        {
            if (task == null) return;

            tasksToAwait.Add(task);
        }

        public void Reset()
        {
            tasksToAwait.Clear();
            cancellationTokenSource = new CancellationTokenSource();
        }

        public void ThrowIfCancellationRequested()
        {
            cancellationTokenSource.Token.ThrowIfCancellationRequested();
        }

        public void Cancel()
        {
            cancellationTokenSource.Cancel();

            Task.WaitAll(tasksToAwait.ToArray());
        }

        public async Task CancelAsync()
        {
            cancellationTokenSource.Cancel();

            try
            {
                await Task.WhenAll(tasksToAwait.ToArray());
            }
            catch (AggregateException ex)
            {
                ex.Handle(p => p is OperationCanceledException);
            }
        }
    }
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage