PLINQ 和 TPL 中的 Lambda 運算式
工作平行程式庫 (TPL) 包含許多採用委派的其中一個 System.Func<TResult> 或 System.Action 系列做為輸入參數的方法。 您可以使用這些委派,將您的自訂程式邏輯傳遞至平行迴圈、工作或查詢。 TPL 和 PLINQ 的程式碼範例會使用 Lambda 運算式,將這些委派的執行個體建立為內嵌程式碼區塊。 本主題將簡單介紹 Func 和 Action,並說明如何在工作平行程式庫和 PLINQ 中使用 Lambda 運算式。
注意:如需一般委派的詳細資訊,請參閱委派 (C# 程式設計手冊) 和 委派 (Visual Basic)。 如需如何在 C# 和 Visual Basic 中使用 Lambda 運算式的詳細資訊,請參閱 Lambda 運算式 (C# 程式設計手冊) 和 Lambda 運算式 (Visual Basic)。
Func 委派
Func 委派封裝了會傳回值的方法。 在 Func 簽章中,最後一個或最右側的型別參數一律會指定傳回型別。 編譯器錯誤的一個常見原因是嘗試將兩個輸入參數傳入 System.Func<T, TResult>;事實上這個型別只接受一個輸入參數。 Framework 類別庫定義了 17 種版本的 Func:System.Func<TResult>、System.Func<T, TResult>、System.Func<T1, T2, TResult> 乃至於 System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>。
Action 委派
System.Action 委派會封裝不會傳回值的方法 (在 Visual Basic 中為 Sub),或會傳回 void 的方法。 在 Action 型別簽章中,型別參數僅代表輸入參數。 如同 Func,Framework 類別庫定義了 17 種版本的 Action,從不具任何型別參數的版本,乃至於具有 16 個型別參數的版本。
範例
Parallel.ForEach<TSource, TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource, ParallelLoopState, TLocal, TLocal>, Action<TLocal>) 方法的下列範例示範如何使用 Lambda 運算式來表示 Func 和 Action 委派。
Imports System.Threading
Imports System.Threading.Tasks
Module ForEachDemo
' Demonstrated features:
' Parallel.ForEach()
' Thread-local state
' Expected results:
' This example sums up the elements of an int[] in parallel.
' Each thread maintains a local sum. When a thread is initialized, that local sum is set to 0.
' On every iteration the current element is added to the local sum.
' When a thread is done, it safely adds its local sum to the global sum.
' After the loop is complete, the global sum is printed out.
' Documentation:
' https://msdn.microsoft.com/en-us/library/dd990270(VS.100).aspx
Private Sub ForEachDemo()
' The sum of these elements is 40.
Dim input As Integer() = {4, 1, 6, 2, 9, 5, _
10, 3}
Dim sum As Integer = 0
Try
' source collection
Parallel.ForEach(input,
Function()
' thread local initializer
Return 0
End Function,
Function(n, loopState, localSum)
' body
localSum += n
Console.WriteLine("Thread={0}, n={1}, localSum={2}", Thread.CurrentThread.ManagedThreadId, n, localSum)
Return localSum
End Function,
Sub(localSum)
' thread local aggregator
Interlocked.Add(sum, localSum)
End Sub)
Console.WriteLine(vbLf & "Sum={0}", sum)
Catch e As AggregateException
' No exception is expected in this example, but if one is still thrown from a task,
' it will be wrapped in AggregateException and propagated to the main thread.
Console.WriteLine("Parallel.ForEach has thrown an exception. THIS WAS NOT EXPECTED." & vbLf & "{0}", e)
End Try
End Sub
End Module
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class ForEachWithThreadLocal
{
// Demonstrated features:
// Parallel.ForEach()
// Thread-local state
// Expected results:
// This example sums up the elements of an int[] in parallel.
// Each thread maintains a local sum. When a thread is initialized, that local sum is set to 0.
// On every iteration the current element is added to the local sum.
// When a thread is done, it safely adds its local sum to the global sum.
// After the loop is complete, the global sum is printed out.
// Documentation:
// https://msdn.microsoft.com/en-us/library/dd990270(VS.100).aspx
static void Main()
{
// The sum of these elements is 40.
int[] input = { 4, 1, 6, 2, 9, 5, 10, 3 };
int sum = 0;
try
{
Parallel.ForEach(
input, // source collection
() => 0, // thread local initializer
(n, loopState, localSum) => // body
{
localSum += n;
Console.WriteLine("Thread={0}, n={1}, localSum={2}", Thread.CurrentThread.ManagedThreadId, n, localSum);
return localSum;
},
(localSum) => Interlocked.Add(ref sum, localSum) // thread local aggregator
);
Console.WriteLine("\nSum={0}", sum);
}
// No exception is expected in this example, but if one is still thrown from a task,
// it will be wrapped in AggregateException and propagated to the main thread.
catch (AggregateException e)
{
Console.WriteLine("Parallel.ForEach has thrown an exception. THIS WAS NOT EXPECTED.\n{0}", e);
}
}
}