Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом примере показано, как использовать локальные переменные потока для хранения и извлечения состояния в каждой отдельной задаче, созданной циклом For . Используя локальные данные потока, можно избежать затрат на синхронизацию большого числа обращений к общему состоянию. Вместо записи в общий ресурс для каждой итерации вы вычисляете и сохраняете значение до завершения всех итераций для задачи. Затем можно написать окончательный результат один раз в общий ресурс или передать его другому методу.
Пример
В следующем примере метод вызывается For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) для вычисления суммы значений в массиве, содержащего один миллион элементов. Значение каждого элемента равно индексу.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
int[] nums = Enumerable.Range(0, 1_000_000).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;
},
subtotal => Interlocked.Add(ref total, subtotal));
Console.WriteLine($"The total is {total:N0}");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
'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(subtotal) Interlocked.Add(total, subtotal))
Console.WriteLine("The total is {0:N0}", total)
Console.WriteLine("Press any key to exit")
Console.ReadKey()
End Sub
End Module
Первые два параметра каждого For метода указывают начальные и конечные значения итерации. В этой перегрузке метода третий параметр — это место, где вы инициализируете локальное состояние. В этом контексте локальное состояние означает переменную, время существования которой начинается непосредственно перед первой итерацией цикла в текущем потоке и заканчивается сразу после последней итерации.
Тип третьего параметра — это Func<TResult>TResult
тип переменной, которая будет хранить локальное состояние потока. Его тип определяется аргументом универсального типа, предоставленным при вызове универсального For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) метода, который в данном случае является Int64. Аргумент типа сообщает компилятору тип временной переменной, которая будет использоваться для хранения состояния потока. В этом примере выражение () => 0
(или Function() 0
в Visual Basic) инициализирует локальную переменную потока до нуля. Если аргумент универсального типа является ссылочным типом или определяемым пользователем типом значения, выражение будет выглядеть следующим образом:
() => new MyClass()
Function() new MyClass()
Четвертый параметр определяет логику цикла. Он должен быть делегатом или лямбда-выражением с сигнатурой Func<int, ParallelLoopState, long, long>
в C# или Func(Of Integer, ParallelLoopState, Long, Long)
в Visual Basic. Первый параметр — это значение счетчика цикла для этой итерации цикла. Второй — это ParallelLoopState объект, который можно использовать для выхода из цикла; этот объект предоставляется Parallel классом для каждой итерации цикла. Третий параметр — это локальная переменная потока. Последний параметр — это тип возвращаемого значения. В этом случае тип — это Int64, потому что он указан в аргументе типа For. Эта переменная называется subtotal
и возвращается лямбда-выражением. Возвращаемое значение используется для инициализации subtotal
для каждой последующей итерации цикла. Этот последний параметр можно также рассматривать как значение, передаваемое каждой итерации, а затем передается делегату после завершения последней localFinally
итерации.
Пятый параметр определяет метод, который вызывается один раз, после завершения всех итераций в определенном потоке. Тип входного аргумента в очередной раз соответствует типу аргумента метода For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) и типу, который возвращает тело лямбда-выражения. В этом примере значение добавляется в переменную в области класса безопасным для потоков образом путем вызова метода Interlocked.Add. Используя локальную переменную потока, мы избегали записи в эту переменную класса при каждой итерации цикла.
Дополнительные сведения об использовании лямбда-выражений см.: Лямбда-выражения в PLINQ и TPL.