Procedura: scrivere un ciclo Parallel.For sempliceHow to: Write a Simple Parallel.For Loop

Questo argomento contiene due esempi che mostrano il metodo Parallel.For.This topic contains two examples that illustrate the Parallel.For method. Il primo usa l'overload del metodo Parallel.For(Int64, Int64, Action<Int64>), mentre il secondo usa l'overload Parallel.For(Int32, Int32, Action<Int32>), ovvero i due overload più semplici del metodo Parallel.For.The first uses the Parallel.For(Int64, Int64, Action<Int64>) method overload, and the second uses the Parallel.For(Int32, Int32, Action<Int32>) overload, the two simplest overloads of the Parallel.For method. È possibile usare questi due overload del metodo Parallel.For quando non è necessario annullare il ciclo, interrompere le iterazioni del ciclo o mantenere qualsiasi stato locale dei thread.You can use these two overloads of the Parallel.For method when you do not need to cancel the loop, break out of the loop iterations, or maintain any thread-local state.

Nota

Questa documentazione usa espressioni lambda per definire delegati in TPL.This documentation uses lambda expressions to define delegates in TPL. Se non si ha familiarità con le espressioni lambda in C# o Visual Basic, vedere Espressioni lambda in PLINQ e TPL.If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda Expressions in PLINQ and TPL.

Il primo esempio calcola le dimensioni dei file in una singola directory.The first example calculates the size of files in a single directory. Il secondo calcola il prodotto di due matrici.The second computes the product of two matrices.

EsempioExample

Questo esempio è una semplice utilità da riga di comando che calcola le dimensioni totali dei file in una directory.This example is a simple command-line utility that calculates the total size of files in a directory. È previsto un singolo percorso di directory come argomento e l'esempio indica il numero e le dimensioni totali dei file presenti nella directory.It expects a single directory path as an argument, and reports the number and total size of the files in that directory. Dopo aver verificato l'esistenza della directory, l'esempio usa il metodo Parallel.For per enumerare i file nella directory e determinarne le dimensioni.After verifying that the directory exists, it uses the Parallel.For method to enumerate the files in the directory and determine their file sizes. Le dimensioni di ogni file vengono quindi aggiunte alla variabile totalSize.Each file size is then added to the totalSize variable. Tenere presente che l'aggiunta viene eseguita chiamando il metodo Interlocked.Add, in modo che sia un'operazione atomica.Note that the addition is performed by calling the Interlocked.Add so that the addition is performed as an atomic operation. In caso contrario, più attività potrebbero tentare di aggiornare la variabile totalSize contemporaneamente.Otherwise, multiple tasks could try to update the totalSize variable simultaneously.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      long totalSize = 0;

      String[] args = Environment.GetCommandLineArgs();
      if (args.Length == 1) {
         Console.WriteLine("There are no command line arguments.");
         return;
      }
      if (! Directory.Exists(args[1])) {
         Console.WriteLine("The directory does not exist.");
         return;
      }

      String[] files = Directory.GetFiles(args[1]);
      Parallel.For(0, files.Length,
                   index => { FileInfo fi = new FileInfo(files[index]);
                              long size = fi.Length;
                              Interlocked.Add(ref totalSize, size);
                   } );
      Console.WriteLine("Directory '{0}':", args[1]);
      Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize);
   }
}
// The example displaysoutput like the following:
//       Directory 'c:\windows\':
//       32 files, 6,587,222 bytes
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim totalSize As Long = 0

      Dim args() As String = Environment.GetCommandLineArgs()
      If args.Length = 1 Then
         Console.WriteLine("There are no command line arguments.")
         Return
      End If
      If Not Directory.Exists(args(1))
         Console.WriteLine("The directory does not exist.")
         Return
      End If

      Dim files() As String = Directory.GetFiles(args(1))
      Parallel.For(0, files.Length,
                   Sub(index As Integer)
                      Dim fi As New FileInfo(files(index))
                      Dim size As Long = fi.Length
                      Interlocked.Add(totalSize, size)
                   End Sub)
      Console.WriteLine("Directory '{0}':", args(1))
      Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize)
   End Sub
End Module
' The example displays output like the following:
'       Directory 'c:\windows\':
'       32 files, 6,587,222 bytes

EsempioExample

Questo esempio usa il metodo Parallel.For per calcolare il prodotto di due matrici.This example uses the Parallel.For method to compute the product of two matrices. L'esempio mostra anche come usare la classe System.Diagnostics.Stopwatch per confrontare le prestazioni di un ciclo parallelo con uno non parallelo.It also shows how to use the System.Diagnostics.Stopwatch class to compare the performance of a parallel loop with a non-parallel loop. Poiché può generare un volume elevato di output, l'esempio permette il reindirizzamento dell'output a un file.Note that, because it can generate a large volume of output, the example allows output to be redirected to a file.

using System;
using System.Diagnostics;
using System.Threading.Tasks;

class MultiplyMatrices
{
    #region Sequential_Loop
    static void MultiplyMatricesSequential(double[,] matA, double[,] matB,
                                            double[,] result)
    {
        int matACols = matA.GetLength(1);
        int matBCols = matB.GetLength(1);
        int matARows = matA.GetLength(0);

        for (int i = 0; i < matARows; i++)
        {
            for (int j = 0; j < matBCols; j++)
            {
                double temp = 0;
                for (int k = 0; k < matACols; k++)
                {
                   temp += matA[i, k] * matB[k, j];
                }
                result[i, j] += temp;
            }
        }
    }
    #endregion

    #region Parallel_Loop
    static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
    {
        int matACols = matA.GetLength(1);
        int matBCols = matB.GetLength(1);
        int matARows = matA.GetLength(0);

        // A basic matrix multiplication.
        // Parallelize the outer loop to partition the source array by rows.
        Parallel.For(0, matARows, i =>
        {
            for (int j = 0; j < matBCols; j++)
            {
                double temp = 0;
                for (int k = 0; k < matACols; k++)
                {
                    temp += matA[i, k] * matB[k, j];
                }
                result[i, j] = temp;
            }
        }); // Parallel.For
    }
    #endregion


    #region Main
    static void Main(string[] args)
    {
        // Set up matrices. Use small values to better view 
        // result matrix. Increase the counts to see greater 
        // speedup in the parallel loop vs. the sequential loop.
        int colCount = 180;
        int rowCount = 2000;
        int colCount2 = 270;
        double[,] m1 = InitializeMatrix(rowCount, colCount);
        double[,] m2 = InitializeMatrix(colCount, colCount2);
        double[,] result = new double[rowCount, colCount2];

        // First do the sequential version.
        Console.Error.WriteLine("Executing sequential loop...");
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        MultiplyMatricesSequential(m1, m2, result);
        stopwatch.Stop();
        Console.Error.WriteLine("Sequential loop time in milliseconds: {0}",
                                stopwatch.ElapsedMilliseconds);

        // For the skeptics.
        OfferToPrint(rowCount, colCount2, result);

        // Reset timer and results matrix. 
        stopwatch.Reset();
        result = new double[rowCount, colCount2];

        // Do the parallel loop.
        Console.Error.WriteLine("Executing parallel loop...");
        stopwatch.Start();
        MultiplyMatricesParallel(m1, m2, result);
        stopwatch.Stop();
        Console.Error.WriteLine("Parallel loop time in milliseconds: {0}",
                                stopwatch.ElapsedMilliseconds);
        OfferToPrint(rowCount, colCount2, result);

        // Keep the console window open in debug mode.
        Console.Error.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
    #endregion

    #region Helper_Methods
    static double[,] InitializeMatrix(int rows, int cols)
    {
        double[,] matrix = new double[rows, cols];

        Random r = new Random();
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                matrix[i, j] = r.Next(100);
            }
        }
        return matrix;
    }

    private static void OfferToPrint(int rowCount, int colCount, double[,] matrix)
    {
        Console.Error.Write("Computation complete. Print results (y/n)? ");
        char c = Console.ReadKey(true).KeyChar;
        Console.Error.WriteLine(c);
        if (Char.ToUpperInvariant(c) == 'Y')
        {
            if (! Console.IsOutputRedirected) Console.WindowWidth = 180;
            Console.WriteLine();
            for (int x = 0; x < rowCount; x++)
            {
                Console.WriteLine("ROW {0}: ", x);
                for (int y = 0; y < colCount; y++)
                {
                    Console.Write("{0:#.##} ", matrix[x, y]);
                }
                Console.WriteLine();
            }
        }
    }
    #endregion
}
Imports System.Diagnostics
Imports System.Threading.Tasks

Module MultiplyMatrices
#Region "Sequential_Loop"
    Sub MultiplyMatricesSequential(ByVal matA As Double(,), ByVal matB As Double(,), ByVal result As Double(,))
        Dim matACols As Integer = matA.GetLength(1)
        Dim matBCols As Integer = matB.GetLength(1)
        Dim matARows As Integer = matA.GetLength(0)

        For i As Integer = 0 To matARows - 1
            For j As Integer = 0 To matBCols - 1
                Dim temp As Double = 0
                For k As Integer = 0 To matACols - 1
                   temp += matA(i, k) * matB(k, j)
                Next
                result(i, j) += temp
            Next
        Next
    End Sub
#End Region

#Region "Parallel_Loop"
    Private Sub MultiplyMatricesParallel(ByVal matA As Double(,), ByVal matB As Double(,), ByVal result As Double(,))
        Dim matACols As Integer = matA.GetLength(1)
        Dim matBCols As Integer = matB.GetLength(1)
        Dim matARows As Integer = matA.GetLength(0)

        ' A basic matrix multiplication.
        ' Parallelize the outer loop to partition the source array by rows.
        Parallel.For(0, matARows, Sub(i)
                                      For j As Integer = 0 To matBCols - 1
                                          Dim temp As Double = 0
                                          For k As Integer = 0 To matACols - 1
                                              temp += matA(i, k) * matB(k, j)
                                          Next
                                          result(i, j) += temp
                                      Next
                                  End Sub)
    End Sub
#End Region

#Region "Main"
    Sub Main(ByVal args As String())
        ' Set up matrices. Use small values to better view 
        ' result matrix. Increase the counts to see greater 
        ' speedup in the parallel loop vs. the sequential loop.
        Dim colCount As Integer = 180
        Dim rowCount As Integer = 2000
        Dim colCount2 As Integer = 270
        Dim m1 As Double(,) = InitializeMatrix(rowCount, colCount)
        Dim m2 As Double(,) = InitializeMatrix(colCount, colCount2)
        Dim result As Double(,) = New Double(rowCount - 1, colCount2 - 1) {}

        ' First do the sequential version.
        Console.Error.WriteLine("Executing sequential loop...")
        Dim stopwatch As New Stopwatch()
        stopwatch.Start()

        MultiplyMatricesSequential(m1, m2, result)
        stopwatch.[Stop]()
        Console.Error.WriteLine("Sequential loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds)

        ' For the skeptics.
        OfferToPrint(rowCount, colCount2, result)

        ' Reset timer and results matrix. 
        stopwatch.Reset()
        result = New Double(rowCount - 1, colCount2 - 1) {}

        ' Do the parallel loop.
        Console.Error.WriteLine("Executing parallel loop...")
        stopwatch.Start()
        MultiplyMatricesParallel(m1, m2, result)
        stopwatch.[Stop]()
        Console.Error.WriteLine("Parallel loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds)
        OfferToPrint(rowCount, colCount2, result)

        ' Keep the console window open in debug mode.
        Console.Error.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub
#End Region

#Region "Helper_Methods"
    Function InitializeMatrix(ByVal rows As Integer, ByVal cols As Integer) As Double(,)
        Dim matrix As Double(,) = New Double(rows - 1, cols - 1) {}

        Dim r As New Random()
        For i As Integer = 0 To rows - 1
            For j As Integer = 0 To cols - 1
                matrix(i, j) = r.[Next](100)
            Next
        Next
        Return matrix
    End Function

    Sub OfferToPrint(ByVal rowCount As Integer, ByVal colCount As Integer, ByVal matrix As Double(,))
        Console.Error.Write("Computation complete. Display results (y/n)? ")
        Dim c As Char = Console.ReadKey(True).KeyChar
        Console.Error.WriteLine(c)
        If Char.ToUpperInvariant(c) = "Y"c Then
            If Not Console.IsOutputRedirected Then Console.WindowWidth = 168
            Console.WriteLine()
            For x As Integer = 0 To rowCount - 1
                Console.WriteLine("ROW {0}: ", x)
                For y As Integer = 0 To colCount - 1
                    Console.Write("{0:#.##} ", matrix(x, y))
                Next
                Console.WriteLine()
            Next
        End If
    End Sub
#End Region
End Module

Quando si parallelizza qualsiasi codice, inclusi i cicli, un obiettivo importante consiste nell'utilizzare i processori quanto più possibile senza eseguire una parallelizzazione eccessiva che faccia sì che l'overhead per l'elaborazione parallela vanifichi qualsiasi vantaggio in termini di prestazioni.When parallelizing any code, including loops, one important goal is to utilize the processors as much as possible without over parallelizing to the point where the overhead for parallel processing negates any performance benefits. In questo esempio specifico viene parallelizzato solo il ciclo esterno, perché nel ciclo interno non vengono eseguite molte attività.In this particular example, only the outer loop is parallelized because there is not very much work performed in the inner loop. La combinazione di una quantità ridotta di attività e di effetti della cache indesiderati può provocare un peggioramento delle prestazioni nei cicli paralleli annidati.The combination of a small amount of work and undesirable cache effects can result in performance degradation in nested parallel loops. Di conseguenza, la parallelizzazione del solo ciclo esterno è il modo migliore per ottimizzare i vantaggi della concorrenza sulla maggior parte dei sistemi.Therefore, parallelizing the outer loop only is the best way to maximize the benefits of concurrency on most systems.

DelegatoThe Delegate

Il terzo parametro di questo overload di For è un delegato di tipo Action<int> in C# o Action(Of Integer) in Visual Basic.The third parameter of this overload of For is a delegate of type Action<int> in C# or Action(Of Integer) in Visual Basic. Un delegato Action, che abbia nessuno, uno o sedici parametri di tipo, restituisce sempre void.An Action delegate, whether it has zero, one or sixteen type parameters, always returns void. In Visual Basic il comportamento di un delegato Action viene definito con un oggetto Sub.In Visual Basic, the behavior of an Action is defined with a Sub. L'esempio usa un'espressione lambda per creare il delegato, ma è possibile creare il delegato anche in altri modi.The example uses a lambda expression to create the delegate, but you can create the delegate in other ways as well. Per altre informazioni, vedere Espressioni lambda in PLINQ e TPL.For more information, see Lambda Expressions in PLINQ and TPL.

Valore di iterazioneThe Iteration Value

Il delegato accetta un singolo parametro di input il cui valore è l'iterazione corrente.The delegate takes a single input parameter whose value is the current iteration. Questo valore di iterazione viene fornito dal runtime e il suo valore iniziale è l'indice del primo elemento nel segmento (partizione) dell'origine elaborata nel thread corrente.This iteration value is supplied by the runtime and its starting value is the index of the first element on the segment (partition) of the source that is being processed on the current thread.

Se si vuole esercitare maggiore controllo sul livello di concorrenza, usare uno degli overload che accetta un parametro di input System.Threading.Tasks.ParallelOptions, come Parallel.For(Int32, Int32, ParallelOptions, Action<Int32,ParallelLoopState>).If you require more control over the concurrency level, use one of the overloads that takes a System.Threading.Tasks.ParallelOptions input parameter, such as: Parallel.For(Int32, Int32, ParallelOptions, Action<Int32,ParallelLoopState>).

Valore restituito e gestione delle eccezioniReturn Value and Exception Handling

For restituisce un oggetto System.Threading.Tasks.ParallelLoopResult al completamento di tutti i thread.For returns a System.Threading.Tasks.ParallelLoopResult object when all threads have completed. Questo valore restituito è utile quando si arresta o si interrompe l'iterazione del ciclo manualmente, perché l'oggetto ParallelLoopResult archivia informazioni come l'ultima iterazione eseguita fino al completamento.This return value is useful when you are stopping or breaking loop iteration manually, because the ParallelLoopResult stores information such as the last iteration that ran to completion. Se si verificano una o più eccezioni in uno dei thread, verrà generato un oggetto System.AggregateException.If one or more exceptions occur on one of the threads, a System.AggregateException will be thrown.

Nel codice di questo esempio il valore restituito di For non viene usato.In the code in this example, the return value of For is not used.

Analisi e prestazioniAnalysis and Performance

È possibile usare la Creazione guidata sessione di prestazioni per visualizzare l'utilizzo CPU nel computer.You can use the Performance Wizard to view CPU usage on your computer. Come esperimento, aumentare il numero di colonne e righe nelle matrici.As an experiment, increase the number of columns and rows in the matrices. Più grandi sono le matrici, maggiore sarà la differenza in termini di prestazioni tra le versioni parallela e sequenziale del calcolo.The larger the matrices, the greater the performance difference between the parallel and sequential versions of the computation. Quando la matrice è di dimensioni ridotte, la versione sequenziale verrà eseguita più rapidamente a causa dell'overhead durante la configurazione del ciclo parallelo.When the matrix is small, the sequential version will run faster because of the overhead in setting up the parallel loop.

Le chiamate sincrone a risorse condivise, come la console o il file system, riducono significativamente le prestazioni di un ciclo parallelo.Synchronous calls to shared resources, like the Console or the File System, will significantly degrade the performance of a parallel loop. Per la misurazione delle prestazioni, provare a evitare chiamate come Console.WriteLine all'interno del ciclo.When measuring performance, try to avoid calls such as Console.WriteLine within the loop.

Compilazione del codiceCompiling the Code

  • Copiare e incollare questo codice in un progetto di Visual Studio 2010.Copy and paste this code into a Visual Studio 2010 project.

Vedere ancheSee Also

For
ForEach
Parallelismo dei datiData Parallelism
Programmazione parallelaParallel Programming