Timeout for Action in Parallel.ForEach iteration

前端 未结 3 2228
[愿得一人]
[愿得一人] 2021-02-14 22:10

I have something similar to this in my code:

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


        
相关标签:
3条回答
  • 2021-02-14 22:56

    I ended up combining both options. It works but I don't know if this is the proper way to do this.

    Solution:

            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();
            });
    

    and in Process() I check for cancellation multiple times:

        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");
    
    0 讨论(0)
  • 2021-02-14 22:58

    Consider adding CancellationToken to your code. This way, at any point you can properly cancel all the operations.

    Then, you can use the CancelAfter() method.

    0 讨论(0)
  • 2021-02-14 23:06

    I ended up with a slightly different implementation. In my case, checking for the CancellationToken within Process() wouldn't work due to potentially long running statements in between checks. For example if my timeout was 5 seconds, and a single statement took say 100 seconds ... I wouldn't know until that statement completed and then was detected by if (token.IsCancellationRequested).

    Here's what I ended up doing

         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"); 
         });
    

    Then within the Process() method, I added further checks on potentially long running statements to allow it to gracefully handle timeouts (as much as possible). So it's only in the worst case that Process() had to be prematurely stopped by Task.Wait() above.

        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.
          ...
        }
    
    0 讨论(0)
提交回复
热议问题