ThrowIfCancellationRequested, похоже, не выдает никаких исключений

У меня есть следующий код:

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;

Task.Factory.StartNew(() =>
{
     if (Console.ReadKey().KeyChar == 'c')
         cts.Cancel();
     Console.WriteLine("press any key to exit");
});

 Parallel.ForEach(list, po, (algo) =>
 {
      algo.Compute(); // this compute lasts 1 minute  
      Console.WriteLine("this job is finished");       
      po.CancellationToken.ThrowIfCancellationRequested();
 });

list содержит несколько элементов. Все методы Compute уже запущены, когда я нажимаю «c».

Когда я нажимаю «c», никаких исключений не возникает. Каждый Compute метод продолжает свое выполнение до нормального завершения.

Я хотел бы остановить/убить все оставшиеся методы Compute, когда я нажимаю "c".


person Patrice Pezillier    schedule 20.03.2014    source источник
comment
Я думаю, вам нужно обрабатывать отмену в методе Unit-Of-Work вручную. Как только поток запущен, он сам по себе. Вам нужно бросить это из потока AFAIK.   -  person Elad Lachmi    schedule 20.03.2014
comment
Да, вы должны передать токен отмены в algo.Compute() и проверить его в цикле этого метода.   -  person Matthew Watson    schedule 20.03.2014
comment
возможный дубликат Как прервать/отменить задачи TPL?   -  person Fedor    schedule 20.03.2014
comment
Просто поместив волшебную строку ThrowIfCancellationRequested где-нибудь в исходный код, ничего не произойдет. Эта строка также должна быть выполнена.   -  person usr    schedule 14.12.2015


Ответы (2)


Отмена так не работает. Это не похоже на вызов Thread.Abort() для немедленного завершения потока.

Для каждого элемента в последовательности ваш код делает:

  1. Вызывает Compute() метод
  2. Дождется завершения
  3. Пишет в консоль о завершении
  4. Проверяет, была ли запрошена отмена, и выбрасывает OperationCanceledException, если да.

Чтобы отменить какую-то задачу, нужно передать CancellationToken вызываемому методу.
Возможно, стоит организовать свои длительные вычисления в виде цикла и проверять, запрашивалась ли отмена на каждом шаге, чтобы остановить ее как можно скорее.

Например, в вашем методе Compute() вы можете выполнить проверку следующим образом:

private void Compute(CancellationToken ct)
{
    while (true)
    {
       ComputeNextStep();
       ct.ThrowIfCancellationRequested();
    }
}
person Fedor    schedule 20.03.2014
comment
Ill add to this that the point of the CancellationToken` должен быть связующим потоком (каламбур) между процессом, который запустил новый поток, и самим потоком. Если бы процесс заполнения мог отменить поток, не было бы необходимости в токене отмены, не так ли? :) Итак, CancellationToken для сигнализации. Вам нужно обработать сигнал. - person Elad Lachmi; 20.03.2014
comment
Да, и еще кое-что... Это также хороший дизайн, так как перед отменой ваш код может захотеть избавиться от некоторых объектов или дескрипторов. Простое уничтожение потока извне может создать проблемы с памятью/ресурсами. Лучше позволить потоку обработать отмену. - person Elad Lachmi; 20.03.2014

Обратите внимание на отмену с помощью po.CancellationToken.IsCancellationRequested и используйте ParallelLoopState.Stop, чтобы остановить Parallel.ForEach:

void Compute(CancellationToken token, ParallelLoopState loopState)
{
    bool more = true;
    while (more)
    {
        if (token.IsCancellationRequested)
        {
            // stop Parallel.ForEach ASAP
            loopState.Stop();
            return;
        }
        // do the calc step
    }
}

// ... 

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;

Task.Factory.StartNew(() =>
{
    if (Console.ReadKey().KeyChar == 'c')
        cts.Cancel();
    Console.WriteLine("press any key to exit");
});

Parallel.ForEach(list, po, (algo, loopState) =>
{
    algo.Compute(po.CancellationToken, loopState); // this compute lasts 1 minute  
    Console.WriteLine("this job is finished");
});
// observe the cancellation again and throw after Parallel.ForEach
po.CancellationToken.ThrowIfCancellationRequested();
person noseratio    schedule 21.03.2014