CancellationTokenSource.Cancel создает исключение

Поскольку я читаю документацию, CancellationTokenSource.Cancel не должен выдавать исключение.
CancellationTokenSource.Cancel

Под вызовом cts.Cancel(); вызывает (не выбрасывает) исключение OperationCanceledException.
Я очень уверен в этом, как будто я комментирую эту строку, тогда последнее OperationCanceledException не выбрасывается.

При активной строке cts.Cancel строка, которая выдает исключение, является t2.Wait(token);
И если у меня есть задержка в cts.Cancel(); затем t2.Wait (токен); не выдает исключение, как только вызывается строка.
t2.Wait (маркер); генерирует это исключение только при запуске cts.Cancel ().
Это правильное поведение?
Если это соответствует, то я могу жить с этим, но я не хочу, чтобы cts.Cancel вызывал исключение.
Я явно сбит с толку - я просто хочу понять поведение, чтобы чувствовать себя комфортно, перенося это в производственную среду.
Прямо сейчас я делаю это с BackGroundWorker, и я подумал, что смогу упростить отслеживание и поддержку, используя ожидание и отмену.

if (token.IsCancellationRequested || ctr == 100000000)
все еще бросает на ctr == 100000000
Я все еще вижу, что внутреннее OperationCanceledException перехватывается, а внешний (нижний) вообще не генерируется.

Что-то не так с этим кодом?
Или это так должно работать?

Без перехвата попытки в Task t2 = Task.Run я получил неперехваченное исключение.
Я думал, что это было бы поймано попыткой поймать t2.Wait, но один вопрос за один раз.

Это консольное приложение в.NET 4.5.

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        Thread.Sleep(1000);
        cts.Cancel();  // this is thowing an exception that is caught on the last catch (OperationCanceledException Ex)
        // according to the documentation this is not supposed
        // if I comment out the cts.Cancel(); then the exeption is not thrown
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
        else
            Console.WriteLine("Cancellation Not requested in Task {0}.",
                                Task.CurrentId);

    }, token);

    //tried this but did not help
    //Task.Run(() =>
    //{
    //    //Thread.Sleep(1000);
    //    if (token.IsCancellationRequested)
    //        Console.WriteLine("Cancellation requested in Task {0}.",
    //                            Task.CurrentId);
    //}, token);
    //Thread.Sleep(1000);
    ////cts.Cancel();


    Task t2 = Task.Run(() =>
    {
        try
        {
            Console.WriteLine("Task t2 started Int32.MaxValue = " + Int32.MaxValue.ToString());
            Thread.Sleep(4000);
            for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
            {
                if (ctr < 100 || ctr % 100000000 == 0)
                {
                    Console.WriteLine(ctr.ToString());

                }
                if (token.IsCancellationRequested || ctr == 100000000)  //  || ctr == 100000000
                {
                    Console.WriteLine("ThrowIfCancellationRequested in t2 Task  {0}.",
                                Task.CurrentId);
                    throw new OperationCanceledException(token);
                    //token.ThrowIfCancellationRequested();
                }
            }
            Console.WriteLine("Task {0} finished.",
                            Task.CurrentId);
        }
        catch (OperationCanceledException Ex)
        {
            //Console.WriteLine(Ex.ToString());
            Console.WriteLine("OperationCanceledException in Task t2 {0}: The operation was cancelled.",
                                Task.CurrentId);
        }
        catch (Exception Ex)
        {
            Console.WriteLine("Task t2 = Task.Run Exception Ex" + Ex.Message);
        }               
    });
    try
    {
        Console.WriteLine("t2.Wait a");
        t2.Wait(token);
    }
    catch (AggregateException e)
    {
        Console.WriteLine("AggregateException");
        foreach (var v in e.InnerExceptions)
            Console.WriteLine(e.Message + " " + v.Message);
    }
    catch (OperationCanceledException Ex)
    {
        //Console.WriteLine(Ex.ToString());
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
    }
    catch (Exception Ex)
    {
        Console.WriteLine(Ex.ToString());
    }
    Console.WriteLine("end");
    Console.ReadLine();
}

3 ответа

Решение

t2.Wait(token); должен бросить примерно через 1 сек, потому что token отменено

Линия

cts.Cancel();

не может быть брошен, если "Cancellation requested in Task {0}." был напечатан на консоли, как вы сказали, что это было.

Да, это ожидаемое поведение: перегрузка Task.Wait токен отмены ожидает, пока:

  • Задача, которую вы ждете, завершена
  • Переданный в аннулировании токен аннулируется.

В последнем случае, когда Task.Wait отмечает, что переданный в токене был отменен, он генерирует исключение OperationCancelledException. Вот стек вызовов, когда исключение срабатывает в вашем случае

    mscorlib.dll!System.Threading.CancellationToken.ThrowOperationCanceledException() Line 505 + 0x4d bytes C#
    mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 642 C#
    mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3284 + 0xf bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3223 + 0x10 bytes C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3129 + 0xc bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(System.Threading.CancellationToken cancellationToken) Line 3064 + 0xe bytes   C#
>   ConsoleApplication1.exe!ConsoleApplication1.Program.Main(string[] args) Line 82 + 0x15 bytes    C#

Что касается вашей второй части вопроса: если вы - удалили try / catch в течение t2 и - вы никогда не отменили токен,

тогда вы окажетесь в своем внешнем AggregateException обработчик (так как вызов t2.Wait бросил бы AggregateException с одним внутренним исключением, которое будет OperationCanceledException что ты бросил.

Я могу ошибаться по этому поводу, но причина, по которой вы получаете это исключение, заключается в том, что вы отменяете ту же задачу, в которую вы поместили оператор отмены. В документации MSDN операции CanceledException говорится следующее:

The exception that is thrown in a thread upon cancellation of an operation that the thread was executing.

Можете ли вы попробовать изменить первое задание, вызовите следующее и убедитесь, что вы все еще получаете исключение: -

Task.Run(() =>
    {
        Thread.Sleep(1000);
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
cts.Cancel();

Полезная ссылка для taskCancellationSource: -

http://johnbadams.wordpress.com/2012/03/10/understanding-cancellationtokensource-with-tasks/

Другие вопросы по тегам