Cómo: Escribir un bucle Parallel.For que tenga variables locales de subproceso
En este ejemplo se muestra cómo utilizar variables locales de subproceso para almacenar y recuperar el estado de cada tarea independiente que se crea en un bucle For. Si se usan datos locales de subproceso, se puede evitar la sobrecarga de sincronizar un número grande de accesos al estado compartido. En lugar de escribir en un recurso compartido en cada iteración, calcula y almacena el valor hasta que se completan todas las iteraciones de la tarea. A continuación, puede escribir el resultado final una vez en el recurso compartido o pasarlo a otro método.
Ejemplo
'How to: Write a Parallel.For Loop That Has Thread-Local Variables
Imports System.Threading
Imports System.Threading.Tasks
Module ForWithThreadLocal
Sub Main()
Dim nums As Integer() = Enumerable.Range(0, 1000000).ToArray()
Dim total As Long = 0
' Use type parameter to make subtotal a Long type. Function will overflow otherwise.
Parallel.For(Of Long)(0, nums.Length, Function() 0, Function(j, [loop], subtotal)
subtotal += nums(j)
Return subtotal
End Function, Function(x) Interlocked.Add(total, x))
Console.WriteLine("The total is {0}", total)
Console.WriteLine("Press any key to exit")
Console.ReadKey()
End Sub
End Module
namespace ThreadLocalFor
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
// Use type parameter to make subtotal a long, not an int
Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
{
subtotal += nums[j];
return subtotal;
},
(x) => Interlocked.Add(ref total, x)
);
Console.WriteLine("The total is {0}", total);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
}
Los dos primeros parámetros de cada método For especifican los valores de iteración inicial y final. En esta sobrecarga del método, el tercer parámetro es donde inicializa el estado local. " Estado local" en este contexto significa una variable cuya duración se extiende desde inmediatamente antes de la primera iteración del bucle en el subproceso actual hasta inmediatamente después de la última iteración.
El tipo del tercer parámetro es Func<TResult>, donde TResult es el tipo de la variable que almacenará el estado local del subproceso. Tenga en cuenta que, en este ejemplo, se usa una versión genérica del método y el parámetro de tipo es long (Long en Visual Basic). El parámetro de tipo indica al compilador el tipo de la variable temporal que se usará para almacenar el estado local del subproceso. La expresión () => 0 (Function() 0 en Visual Basic) de este ejemplo significa que la variable local de subproceso se inicializa en cero. Si el parámetro de tipo es un tipo de referencia o un tipo de valor definido por el usuario, este ejemplo de Func se parecería al siguiente:
() => new MyClass()
Function() new MyClass()
El cuarto parámetro de tipo es donde define la lógica del bucle. IntelliSense muestra que tiene un tipo de Func<int, ParallelLoopState, long, long> o Func(Of Integer, ParallelLoopState, Long, Long). La expresión lambda espera tres parámetros de entrada en este mismo orden que corresponde a estos tipos. El último parámetro de tipo es el tipo devuelto. En este caso, el tipo es long porque es lo que se especificó en el parámetro de tipo For. Llamamos a esa variable subtotal en la expresión lambda y la devolvemos. El valor devuelto se utiliza para inicializar el subtotal en cada iteración subsiguiente. También puede considerar este último parámetro simplemente como un valor que se pasa a cada iteración y después al delegado localFinally cuando se completa la última iteración.
El quinto parámetro es donde se define el método al que se llamará una vez, cuando todas las iteraciones de este subproceso se hayan completado. El tipo del parámetro de entrada corresponde de nuevo al parámetro de tipo del método For y al tipo que devuelve la expresión lambda del cuerpo. En este ejemplo, el valor se agrega a una variable en el ámbito de clase de una manera segura para subprocesos. Al usar una variable local de subproceso, hemos evitado escribir en esta variable de clase en cada iteración de cada subproceso.
Para obtener más información sobre cómo utilizar las expresiones lambda, vea Expresiones lambda en PLINQ y TPL.
Vea también
Conceptos
Paralelismo de datos (Task Parallel Library)