Тайм-аут для параллельных действий.Итерация ForEach

у меня есть что-то похожее на это в моем коде:

Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    Process(item);
});

дело в том, что я делаю кучу вещей внутри Process() метод (подключение к файловому ресурсу, разбор файла, сохранение в БД и т. д.), И я беспокоюсь, что что-то может пойти не так во время этого процесса, что итерация никогда не закончится...может ли это когда-нибудь случится?

есть ли способ установить тайм-аут для Process() метод, чтобы избежать в конечном итоге зомби нити?

обновление:

самый простой способ, который я нашел для установки таймаута, - это добавление миллисекунд в CancellationTokenSource или вызов Wait() метод на задаче.

#1
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    var cts = new CancellationTokenSource(2000);
    Task task = Task.Factory.StartNew(() => Process(item), cts.Token);
});
#2
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    Task task = Task.Factory.StartNew(() => Process(item));
    task.Wait(2000);
});

проблема в том, что ни один из этих вариантов могут отменить Process() метод. Мне нужно проверить что-то в Process() способ?

3 ответов


попробуйте добавить CancellationToken в ваш код. Таким образом, в любой момент Вы можете отменить все операции.

затем, вы можете использовать CancelAfter() метод.


Я закончил тем, что объединил оба варианта. Это работает, но я не знаю, является ли это правильным способом сделать это.

устранение:

        Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
        {
                var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
                var token = tokenSource.Token;

                Task task = Task.Factory.StartNew(() => Process(item, token), token);
                task.Wait();
        });

и Process() Я проверяю отмену несколько раз:

    private void Process(MyItem item, CancellationToken token)
    {
        try
        {
            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...sentences

            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...more sentences

            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...etc
        }
        catch(Exception ex)
            Console.WriteLine("Operation cancelled");

Я закончил с немного другой реализацией. В моем случае, проверка CancellationToken внутри Process() не будет работать из-за потенциально длительных операторов между проверками. Например, если мой тайм-аут составлял 5 секунд, а один оператор занял, скажем, 100 секунд ... Я не знал бы, пока это утверждение не будет завершено, а затем обнаружено if (token.IsCancellationRequested).

вот что я закончил делать

     Parallel.ForEach(myList, (item) =>
     {
         Task task = Task.Factory.StartNew(() =>
         {
             Process(item));
         });

         if(task.Wait(10000)) // Specify overall timeout for Process() here
             Console.WriteLine("Didn't Time out. All's good!"); 
         else
             Console.WriteLine("Timed out. Leave this one and continue with the rest"); 
     });

затем в Process() метод, я добавил дополнительные проверки на потенциально длительные операторы, позволяющие ему изящно обрабатывать тайм-ауты (насколько это возможно). Так что только в худшем случае процесс() должен был быть преждевременно остановлен Task.Wait() выше.

    private void Process(MyItem item)
    {
      ...
        cmd.CommandTimeout = 5; // The total of timeouts within Process() were 
                                // set to be less than the total Task.Wait duration.
                                // Unfortunately some potentially long running methods 
                                // don't have a built in timeout.
      ...
    }