HOW TO:撰寫含有執行緒區域變數的 Parallel.For 迴圈
此範例說明如何使用執行緒區域變數,儲存及擷取 For 迴圈所建立之每項工作的狀態。 使用執行緒區域資料,可讓您避免因同步處理大量的共用狀態存取而產生額外負荷。 您可以計算並儲存值,直到工作的所有反覆運算完成為止,而無須在每次反覆運算時寫入至共用資源。 接著,您可以將最終結果寫入至共用資源,或將其傳遞至其他方法。
範例
'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();
}
}
}
每個 For 方法的前兩個參數,都分別會指定開始與結束的反覆項目值。 在方法的這個多載中,第三個參數是用以初始化區域狀態的項目。" 在此內容中,「區域狀態」表示存留期始於目前執行緒上之迴圈的第一次反覆運算之前,終於最後一次反覆運算之後的變數。
第三個參數的型別是 Func<TResult>,其中 TResult 是將會儲存執行緒區域狀態的變數型別。 請注意,此範例中使用的是泛型版的方法,型別參數為 long (Visual Basic 中為 Long)。型別參數會告知編譯器,將用以儲存執行緒區域狀態之暫存變數的型別。 此範例中的 () => 0 (Visual Basic 中為 Function() 0) 運算式表示執行緒區域變數已初始化為零。 如果型別參數是參考型別或使用者定義的實值型別,則此 Func 將如下所示:
() => new MyClass()
Function() new MyClass()
第四個型別參數是用以定義迴圈邏輯的項目。 IntelliSense 顯示它具有 Func<int, ParallelLoopState, long, long> 或 Func(Of Integer, ParallelLoopState, Long, Long) 的型別。 Lambda 運算式會預期三個順序相同、對應於這些型別的輸入參數。 最後一個型別參數是傳回型別。 在此案例中型別為 long,因為這是我們在 For 型別參數中指定的型別。 我們在 Lambda 運算式中呼叫該變數 subtotal,然後加以傳回。 傳回值可用以初始化每次後續反覆運算的小計。 您也可以直接將這最後一個參數視為會傳遞至每個反覆項目,然後在最後一個反覆項目完成時傳遞至 localFinally 委派的值。
第五個參數用以定義要在此執行緒上所有的反覆項目完成後呼叫一次的方法。 輸入參數的型別會重新對應至 For 方法的型別參數,和主體 Lambda 運算式所傳回的型別。 在此範例中,會以安全執行緒的方式將值新增至類別範圍的變數中。 透過執行緒區域變數,我們無須在每個執行緒的每次反覆運算時寫入至此類別變數。
如需 Lambda 運算式使用方式的詳細資訊,請參閱PLINQ 和 TPL 中的 Lambda 運算式。