Partager via


Procédure : gérer des exceptions dans des boucles parallèles

Les surcharges Parallel.For et Parallel.ForEach ne possèdent pas de mécanisme permettant de gérer les exceptions. En cela, elles sont similaires aux boucles for et foreach normales (For et For Each en Visual Basic). Une exception non gérée provoque l’arrêt de la boucle dès que toutes les itérations qui s’exécutent en même temps sont terminées.

Quand vous ajoutez votre propre logique de gestion des exceptions à des boucles parallèles, gérez le cas dans lequel de telles exceptions peuvent être levées simultanément sur plusieurs threads et le cas dans lequel une exception levée sur un thread provoque la levée d'une autre exception sur un autre thread. Vous pouvez gérer les deux cas en encapsulant toutes les exceptions de la boucle dans un System.AggregateException. L'exemple suivant montre une méthode possible.

Notes

Quand l’option « Uniquement mon code » est activée, Visual Studio, dans certains cas, peut s’arrêter sur la ligne qui lève l’exception et afficher un message d’erreur indiquant que l’exception n’est pas gérée par le code utilisateur. Cette erreur est sans gravité. Vous pouvez appuyer sur F5 pour continuer et voir le comportement de gestion des exceptions qui est illustré dans l'exemple ci-dessous. Pour empêcher Visual Studio de s'arrêter sur la première erreur, il suffit de désactiver la case à cocher Uniquement mon code sous Outils, Options, Débogage, Général.

Exemple

Dans cet exemple, toutes les exceptions sont interceptées, puis encapsulées dans une System.AggregateException qui est ensuite levée. L'appelant peut décider des exceptions à gérer.

public static partial class Program
{
    public static void ExceptionTwo()
    {
        // Create some random data to process in parallel.
        // There is a good probability this data will cause some exceptions to be thrown.
        byte[] data = new byte[5_000];
        Random r = Random.Shared;
        r.NextBytes(data);

        try
        {
            ProcessDataInParallel(data);
        }
        catch (AggregateException ae)
        {
            var ignoredExceptions = new List<Exception>();
            // This is where you can choose which exceptions to handle.
            foreach (var ex in ae.Flatten().InnerExceptions)
            {
                if (ex is ArgumentException) Console.WriteLine(ex.Message);
                else ignoredExceptions.Add(ex);
            }
            if (ignoredExceptions.Count > 0)
            {
                throw new AggregateException(ignoredExceptions);
            }
        }

        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void ProcessDataInParallel(byte[] data)
    {
        // Use ConcurrentQueue to enable safe enqueueing from multiple threads.
        var exceptions = new ConcurrentQueue<Exception>();

        // Execute the complete loop and capture all exceptions.
        Parallel.ForEach(data, d =>
        {
            try
            {
                // Cause a few exceptions, but not too many.
                if (d < 3) throw new ArgumentException($"Value is {d}. Value must be greater than or equal to 3.");
                else Console.Write(d + " ");
            }
            // Store the exception and continue with the loop.
            catch (Exception e)
            {
                exceptions.Enqueue(e);
            }
        });
        Console.WriteLine();

        // Throw the exceptions here after the loop completes.
        if (!exceptions.IsEmpty)
        {
            throw new AggregateException(exceptions);
        }
    }
}
' How to: Handle Exceptions in Parallel Loops

Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks

Module ExceptionsInLoops

    Sub Main()

        ' Create some random data to process in parallel.
        ' There is a good probability this data will cause some exceptions to be thrown.
        Dim data(1000) As Byte
        Dim r As New Random()
        r.NextBytes(data)

        Try
            ProcessDataInParallel(data)
        Catch ae As AggregateException
            Dim ignoredExceptions As New List(Of Exception)
            ' This is where you can choose which exceptions to handle.
            For Each ex As Exception In ae.Flatten().InnerExceptions
                If (TypeOf (ex) Is ArgumentException) Then
                    Console.WriteLine(ex.Message)
                Else
                    ignoredExceptions.Add(ex)
                End If
            Next
            If ignoredExceptions.Count > 0 Then
                Throw New AggregateException(ignoredExceptions)
            End If
        End Try
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub
    Sub ProcessDataInParallel(ByVal data As Byte())

        ' Use ConcurrentQueue to enable safe enqueueing from multiple threads.
        Dim exceptions As New ConcurrentQueue(Of Exception)

        ' Execute the complete loop and capture all exceptions.
        Parallel.ForEach(Of Byte)(data, Sub(d)
                                            Try
                                                ' Cause a few exceptions, but not too many.
                                                If d < 3 Then
                                                    Throw New ArgumentException($"Value is {d}. Value must be greater than or equal to 3")
                                                Else
                                                    Console.Write(d & " ")
                                                End If
                                            Catch ex As Exception
                                                ' Store the exception and continue with the loop. 
                                                exceptions.Enqueue(ex)
                                            End Try
                                        End Sub)
        Console.WriteLine()
        ' Throw the exceptions here after the loop completes.
        If exceptions.Count > 0 Then
            Throw New AggregateException(exceptions)
        End If
    End Sub
End Module

Voir aussi