Catching errors in parallel foreach loops

With parallel foreach loops, developers can wrap the loop in a try catch statement. Care needs to be taken, however, because the Parallel.ForEach will throw AggregatedException, which has the exceptions it encounters over several threads rolled into one.

Getting ready

We will create a List<string> object that contains a collection of machine IP addresses. The Parallel.ForEach loop will check the IP addresses to see whether the machines on the other end of the given IP are alive. It does this by pinging the IP address. The method that performs the Parallel.ForEach loop will also be given the minimum required alive machines as an integer value. If the minimum number of machines alive is not met, an exception is thrown.

How to do it…

  1. In the Recipes class, add a method called CheckClientMachinesOnline() that takes as parameters a List<string> collection of IP addresses and an integer that specifies the minimum number of machines required to be online. Add a second method called MachineReturnedPing() that will receive an IP address to ping. For our purpose, we will just return false to mimic a dead machine (the ping to the IP address timed out):
    public class Recipes
    {
        public void CheckClientMachinesOnline(List<string> ipAddresses, int minimumLive)
        {        
    
        }   
    
        private bool MachineReturnedPing(string ip)
        {            
            return false;
        } 
    }
  2. Inside the CheckClientMachinesOnline() method, add the Parallel.ForEach loop and create the ParallelOptions variable, which will specify the degree of parallelism. Wrap all this code inside a try catch statement and catch AggregateException:
    try
    {
        int machineCount = ipAddresses.Count();                
        var options = new ParallelOptions();
        options.MaxDegreeOfParallelism = machineCount;
        int deadMachines = 0;
    
        Parallel.ForEach(ipAddresses, options, ip =>
        {
            
        });
    }
    catch (AggregateException aex)
    {
        WriteLine("An AggregateException has occurred");
        throw;
    }
  3. Inside the Parallel.ForEach loop, write the code to check whether the machine is online by calling the MachineReturnedPing() method. In our example, this method will always return false. You will notice that we are keeping track of the offline machine count via the Interlocked.Increment method. This is just a way of incrementing a variable across the threads of the Parallel.ForEach loop:
    if (MachineReturnedPing(ip))
    {
    
    }
    else
    {                        
        if (machineCount - Interlocked.Increment(ref deadMachines) < minimumLive)
        {
            WriteLine($"Machines to check = {machineCount}");
            WriteLine($"Dead machines = {deadMachines}");
            WriteLine($"Minimum machines required = {minimumLive}");
            WriteLine($"Live Machines = {machineCount - deadMachines}");
                  
            throw new Exception($"Minimum machines requirement of {minimumLive} not met");
        }
    }
  4. If you have added all the code correctly, your Recipes class will look like this:
    public class Recipes
    {
        public void CheckClientMachinesOnline(List<string> ipAddresses, int minimumLive)
        {        
            try
            {
                int machineCount = ipAddresses.Count();                
                var options = new ParallelOptions();
                options.MaxDegreeOfParallelism = machineCount;
                int deadMachines = 0;
    
                Parallel.ForEach(ipAddresses, options, ip =>
                {
                    if (MachineReturnedPing(ip))
                    {
    
                    }
                    else
                    {                        
                        if (machineCount - Interlocked.Increment(ref deadMachines) < minimumLive)
                        {
                            WriteLine($"Machines to check = {machineCount}");
                            WriteLine($"Dead machines = {deadMachines}");
                            WriteLine($"Minimum machines required = {minimumLive}");
                            WriteLine($"Live Machines = {machineCount - deadMachines}");
                    
                            throw new Exception($"Minimum machines requirement of {minimumLive} not met");
                        }
                    }
                });
            }
            catch (AggregateException aex)
            {
                WriteLine("An AggregateException has occurred");
                throw;
            }
        }   
    
        private bool MachineReturnedPing(string ip)
        {            
            return false;
        } 
    }
  5. In the console application, create the List<string> object to store a collection of dummy IP addresses. Instantiate your Recipes class and call the CheckClientMachinesOnline() method, passing the collection of IP addresses and a minimum number of machines required to be online to it:
    List<string> ipList = new List<string>();
    for (int i = 0; i <= 10; i++)
    {
        ipList.Add($"10.0.0.{i.ToString()}");
    }
    
    try
    {
        Chapter7.Recipes oRecipe = new Chapter7.Recipes();
        oRecipe.CheckClientMachinesOnline(ipList, 2);
    }
    catch (Exception ex)
    {
        WriteLine(ex.InnerException.Message);
    }
    ReadLine();
  6. Run your application and review the output in the console window:
    How to do it…

How it works…

From the console window output, you can see that the minimum number of machines required to be online was not achieved. The application then threw an exception and caught it from the Parallel.ForEach loop. Being able to handle exceptions in parallel loops such as this one is essential to maintain the stability of your application by being able to handle exceptions as they occur.

We encourage you to play around a little with the Parallel.ForEach loop and drill into some of the inner methods of the AggregareException class to really understand it better.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.124.177