Why is the task not canceled when I call the CancelllationTokenSource Cancel method async method?

I created a little wrapper around CancellationTokenand CancellationTokenSource. The problem is that the method CancelAsync CancellationHelperdoes not work as expected.

I have a problem with the method ItShouldThrowAExceptionButStallsInstead. To cancel a running task, it calls await coordinator.CancelAsync();, but the task is not actually canceled and does not throw an exception ontask.Wait

ItWorksWellAndThrowsExceptionseems to work well, and it uses coordinator.Cancelone that is not asynchronous at all.

The question is, why the task is not canceled when I call the CancellationTokenSourceCancel method in the async method?

Do not let waitHandleyou get confused, this is only to prevent the completion of the task earlier.

Let the code speak for itself:

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);
            }
        }
    }
}
+4
1

.Net .

, , CancellationTokenSource, , CancellationToken, , ( CancellationToken, , ).

Task.Run CancellationToken , , , , .

, CancellationToken:

var task = Task.Run(() =>
{
    token.ThrowIfCancellationRequested();
}, token);

ManualResetEvent, CancellationToken. CancellationToken, reset:

token.Register(() => waitHandle.Set())
+6

All Articles