2016-08-28 2 views
0

Одним из членов ParallelOptions является CancellationToken, значение которого предназначено для доступа к функции лямбда Parallel.ForEach.ParallelOptions.CancellationToken кажется бесполезным

Использование этого требует создания экземпляра CacellationToken перед вызовом Parallel.ForEach, поэтому почему эта локальная переменная не может быть доступна непосредственно внутри функции Lambda для ForEach?

например. вместо:

var ct = new CancellationToken(); 
var options = new ParallelOptions { CancellationToken = ct } 

Parallel.ForEach(source, options, (item) => 
{ 
    options.ct.ThrowIfCancellationRequested(); 
}) 

почему я не могу просто использовать:

var ct = new CancellationToken(); 

Parallel.ForEach(source, (item) => 
{ 
    ct.ThrowIfCancellationRequested(); 
}) 

Это просто удобное место, чтобы спрятать маркер, или есть какая-то основная причина для этого проекта?

+0

Что хорошего в ParallelOptions, так это то, что вы * не * должны сами проверить маркер отмены. Параллель уже делает это за вас. Итак, нет, это не бесполезно :) –

+0

@ HansPassant - вы правы, как ответил Джон ниже. Я не предполагал, потому что в документации MSDN была явная проверка на отмену, даже если это не обязательно (как вы говорите) - пример, который они дают, проверяет право в конце цикла, делая его избыточным. – afuna

ответ

1

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

Например:

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

class Test 
{ 
    static void Main() 
    { 
     var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)); 
     var items = Enumerable.Range(0, 100).ToList(); 
     Parallel.ForEach(items, new ParallelOptions { CancellationToken = cts.Token }, 
      item => 
      { 
       Console.WriteLine(item); 
       Thread.Sleep(1000); 
      }); 
    } 
} 

Это закончат через три секунды с OperationCanceledException (а не AggregateException вы получите, если один/несколько отдельных задач, не удалось) - но тело само действие Безразлично» t знать о токене отмены. Конечно, если у вас есть дорогостоящая операция, которую вы не хотите излишне заполнять для обрабатываемых финальных элементов, то может самостоятельно контролировать токен, но в противном случае вы можете просто сообщить об этом Parallel.ForEach и самому выбросить исключение.

+0

Действительно ли это так? ForEach смотрит на CancellationRequested и делает ли эквивалент ThrowIfCancellationRequested, не сказав вам? Пример MSDN для отмены ForEach (msdn.microsoft.com/en-us/library/ee256691(v=vs.110).aspx) использует явное ThrowIfCancellationRequested с циклом - вы говорите, что он избыточен? – afuna

+0

@afuna: Не обязательно избыточно - если ваше тело действия выполняет несколько действий, и вы хотите остановиться между ними, если была запрошена отмена, тогда вы * можете * хотеть контролировать это, но вы не * имеете *, поскольку я «Я показал в моем примере. –

+0

Спасибо за разъяснение. Документация немного отсутствует в этом. Тем не менее, есть еще одно отличие, которое я заметил после перечитывания MSDN - токен будет вызывать прямое исключение OperationCancelled, как описано в исключении AggregateException. Вы хотите добавить этот лакомый кусочек к своему ответу, и я пометю его как принятый? – afuna