任務並行庫(TPL)提供資料流元件,以協助增加並行應用程式的健全性。 這些數據流元件統稱為 TPL 數據流連結庫。 此數據流模型藉由提供在同一進程內的訊息傳遞,來促進基於演員的程式設計,適用於粗粒度數據流和管道任務。 數據流元件是以 TPL 的類型和排程基礎結構為基礎,並與 C#、Visual Basic 和 F# 語言支援進行異步程式設計整合。 這些資料流元件在您需要多個作業異步通訊或想在資料可用時進行處理時非常有用。 例如,請考慮處理 Web 相機影像資料的應用程式。 藉由使用資料流模型,應用程式可以在影像幀可用時進行處理。 例如,如果應用程式藉由執行光線校正或紅眼減少來增強影像框架,您可以建立數據流元件的 管線 。 管線的每個階段都可能會使用更粗略的平行處理原則功能,例如 TPL 所提供的功能來轉換影像。
本檔提供 TPL 數據流連結庫的概觀。 它描述程式設計模型、預先定義的數據流區塊類型,以及如何設定數據流區塊以符合應用程式的特定需求。
備註
TPL 資料流連結庫 (System.Threading.Tasks.Dataflow 命名空間) 不會與 .NET 一起散發。 若要在 Visual Studio 中安裝 System.Threading.Tasks.Dataflow 命名空間,請開啟您的專案,從 [專案] 功能表選擇 [管理 NuGet 套件],然後在線搜尋 System.Threading.Tasks.Dataflow 套件。 或者,若要使用 .NET Core CLI安裝它,請執行 dotnet add package System.Threading.Tasks.Dataflow。
程序設計模型
TPL 數據流連結庫提供訊息傳遞和平行處理 CPU 密集和 I/O 密集應用程式的基礎,這些應用程式具有高輸送量和低延遲。 它同時賦予您明確控制數據在系統中緩衝和流動的方式。 若要進一步了解數據流程序設計模型,請考慮以異步方式從磁碟載入映像並建立這些映像的複合應用程式。 傳統的程式設計模型通常需要您使用回呼和同步處理物件,例如鎖定,以協調工作及存取共享數據。 藉由使用數據流程序設計模型,您可以建立數據流物件,以在從磁碟讀取影像時處理映像。 在數據流模型中,您會宣告數據在可用時如何處理數據,以及數據之間的任何相依性。 因為運行時間會管理數據之間的相依性,所以您通常可以避免同步存取共享數據的需求。 此外,由於運行時會根據數據的異步抵達來排程工作,因此數據流可以透過有效管理基礎線程來改善回應性和吞吐量。 如需使用數據流程序設計模型在 Windows Forms 應用程式中實作影像處理的範例,請參閱 逐步解說:在 Windows Forms 應用程式中使用數據流。
來源和目標
TPL 數據流連結庫是由 數據流區塊所組成,這些區塊是緩衝處理數據的數據結構。 TPL 定義三種數據流區塊: 來源區塊、 目標區塊和 傳播器區塊。 來源區塊會做為數據源,而且可以從中讀取。 目標區塊可作為數據的接收者,而且可以寫入。 傳播器區塊同時做為來源區塊和目標區塊,而且可以讀取和寫入。 TPL 會 System.Threading.Tasks.Dataflow.ISourceBlock<TOutput> 定義介面來表示來源、 System.Threading.Tasks.Dataflow.ITargetBlock<TInput> 表示目標,以及 System.Threading.Tasks.Dataflow.IPropagatorBlock<TInput,TOutput> 表示傳播器。 IPropagatorBlock<TInput,TOutput> 繼承自 ISourceBlock<TOutput>、 與 ITargetBlock<TInput>。
TPL 數據流庫提供數個預先定義的數據流區塊類型,這些類型實作了ISourceBlock<TOutput>、ITargetBlock<TInput> 和 IPropagatorBlock<TInput,TOutput> 介面。 這些數據流區塊類型會在<
連接區塊
您可以將數據流區塊連線到形成 管線,這些管線是數據流區塊的線性序列,或是數據流區塊圖形 的網路。 管線是一種網路形式。 在管線或網路中,來源會隨著該數據變成可用,以異步方式將數據傳播至目標。 方法會將 ISourceBlock<TOutput>.LinkTo 來源數據流區塊鏈接至目標區塊。 來源可以連結至零個或多個目標;目標可以從零或多個來源連結。 您可以同時在管線或網路中新增或移除資料流區塊。 預先定義的數據流區塊類型會處理連結和取消連結的所有線程安全性層面。
如需連接數據流區塊以形成基本管線的範例,請參閱 逐步解說:建立數據流管線。 如需將數據流區塊連線到形成更複雜的網路範例,請參閱 逐步解說:在 Windows Forms 應用程式中使用數據流。 如需在來源提供目標訊息之後,將目標與來源取消連結的範例,請參閱 如何:取消鏈接數據流區塊。
篩選
當您呼叫 ISourceBlock<TOutput>.LinkTo 方法以將來源連結至目標時,您可以提供委派,以根據該訊息的值來判斷目標區塊是否接受或拒絕訊息。 此篩選機制是保證數據流區塊只接收特定值的實用方式。 對於大部分預先定義的數據流區塊類型,如果來源區塊連接到多個目標區塊,當目標區塊拒絕訊息時,來源會將該訊息提供給下一個目標。 來源提供訊息給目標的順序是由來源所定義,而且會根據來源的類型而有所不同。 在一個目標接受該訊息之後,大部分的來源區塊類型都會停止提供訊息。 此規則的其中一個例外是 BroadcastBlock<T> 類別,此類別會將每個訊息提供給所有目標,即使某些目標拒絕訊息也一樣。 如需使用篩選只處理特定訊息的範例,請參閱 逐步解說:在 Windows Forms 應用程式中使用數據流。
這很重要
由於每個預先定義的來源數據流區塊類型都保證訊息會依接收的順序傳播出來,因此來源區塊必須先從來源區塊讀取每個訊息,來源區塊才能處理下一個訊息。 因此,當您使用篩選將多個目標連線至來源時,請確定至少有一個目標區塊接收每個訊息。 否則,您的應用程式可能會死結。
訊息傳遞
數據流程序設計模型與 訊息傳遞的概念相關,其中程式的獨立元件會藉由傳送訊息彼此通訊。 在應用程式元件之間傳播訊息的其中一種方法是呼叫 Post (同步) 和 SendAsync (異步) 方法,以將訊息傳送至目標數據流區塊,以及 ReceiveReceiveAsync、、 和 TryReceive 方法,以接收來源區塊的訊息。 您可以將輸入數據傳送至前端節點(目標區塊),以及從管線的終端節點或網路的終端節點(一或多個來源區塊)接收輸出數據,將這些方法與數據流管線或網路網路結合。 您也可以使用 Choose 方法,從提供的來源中選擇第一個有可用數據的來源進行讀取,並對這些數據執行動作。
來源區塊會藉由呼叫 ITargetBlock<TInput>.OfferMessage 方法來提供數據給目標區塊。 目標區塊會以三種方式之一回應提供的訊息:它可以接受訊息、拒絕訊息或延後訊息。 當目標接受訊息時, OfferMessage 方法會傳 Accepted回 。 當目標拒絕訊息時, OfferMessage 方法會傳 Declined回 。 當目標要求它不再從來源接收任何訊息時, OfferMessage 會傳 DecliningPermanently回 。 預先定義的來源區塊類型在收到這類傳回值之後,不會向連結的目標提供訊息,而且會自動取消與這類目標的連結。
當目標區塊延後訊息以供稍後使用時, OfferMessage 方法會傳 Postponed回 。 延後訊息的目標區塊可以稍後呼叫 ISourceBlock<TOutput>.ReserveMessage 方法來嘗試保留提供的訊息。 此時,訊息仍可供目標區塊使用,或訊息已由另一個目標採用。 當目標區塊稍後需要訊息或不再需要訊息時,它會分別呼叫 ISourceBlock<TOutput>.ConsumeMessage 或 ReleaseReservation 方法。 訊息保留通常由以非搶佔模式運作的資料流區塊種類使用。 本檔稍後會說明非貪婪模式。 目標區塊可以使用 ISourceBlock<TOutput>.ConsumeMessage 方法嘗試直接消耗延後訊息,而不是保留該延後訊息。
數據流區塊完成
數據流區塊也支援 完成的概念。 處於已完成狀態的資料流程區塊不會執行任何進一步的工作。 每個數據流區塊都有一個相關聯的 System.Threading.Tasks.Task 物件,稱為 完成工作,代表區塊的完成狀態。 因為您可以等候 Task 物件完成,因此透過使用完成任務,您可以等候資料流網路的一或多個終端節點完成。 介面 IDataflowBlock 定義了方法 Complete,此方法通知數據流區塊有完成的請求,而屬性 Completion 則返回數據流區塊的完成任務。 ISourceBlock<TOutput>和ITargetBlock<TInput>都繼承IDataflowBlock介面。
有兩種方式可以判斷數據流區塊是否已完成,而不會發生錯誤、發生一或多個錯誤,或已取消。 第一種方式是在完成工作的任務上呼叫 Task.Wait 方法,並在try-catch區塊(在 Visual Basic 中為Try-Catch)中進行。 下列範例會建立一個ActionBlock<TInput> 物件,如果輸入值小於零,則會擲回 ArgumentOutOfRangeException 。 當範例在完成工作時呼叫 AggregateException,Wait 會被擲回。 透過 ArgumentOutOfRangeException 物件的 InnerExceptions 屬性來存取 AggregateException。
// Create an ActionBlock<int> object that prints its input
// and throws ArgumentOutOfRangeException if the input
// is less than zero.
var throwIfNegative = new ActionBlock<int>(n =>
{
Console.WriteLine($"n = {n}");
if (n < 0)
{
throw new ArgumentOutOfRangeException();
}
});
// Post values to the block.
throwIfNegative.Post(0);
throwIfNegative.Post(-1);
throwIfNegative.Post(1);
throwIfNegative.Post(-2);
throwIfNegative.Complete();
// Wait for completion in a try/catch block.
try
{
throwIfNegative.Completion.Wait();
}
catch (AggregateException ae)
{
// If an unhandled exception occurs during dataflow processing, all
// exceptions are propagated through an AggregateException object.
ae.Handle(e =>
{
Console.WriteLine($"Encountered {e.GetType().Name}: {e.Message}");
return true;
});
}
/* Output:
n = 0
n = -1
Encountered ArgumentOutOfRangeException: Specified argument was out of the range
of valid values.
*/
' Create an ActionBlock<int> object that prints its input
' and throws ArgumentOutOfRangeException if the input
' is less than zero.
Dim throwIfNegative = New ActionBlock(Of Integer)(Sub(n)
Console.WriteLine("n = {0}", n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Sub)
' Post values to the block.
throwIfNegative.Post(0)
throwIfNegative.Post(-1)
throwIfNegative.Post(1)
throwIfNegative.Post(-2)
throwIfNegative.Complete()
' Wait for completion in a try/catch block.
Try
throwIfNegative.Completion.Wait()
Catch ae As AggregateException
' If an unhandled exception occurs during dataflow processing, all
' exceptions are propagated through an AggregateException object.
ae.Handle(Function(e)
Console.WriteLine("Encountered {0}: {1}", e.GetType().Name, e.Message)
Return True
End Function)
End Try
' Output:
' n = 0
' n = -1
' Encountered ArgumentOutOfRangeException: Specified argument was out of the range
' of valid values.
'
此範例示範例外狀況在執行數據流區塊的委派中未處理的情況。 建議您在這類區塊主體中處理例外。 但是,如果您無法執行此操作,則封鎖的行為就像已取消一樣,並且不會處理傳入的訊息。
明確取消數據流區塊時,AggregateException 物件在 OperationCanceledException 屬性中包含 InnerExceptions。 如需數據流取消的詳細資訊,請參閱 啟用取消 一節。
判斷數據流區塊完成狀態的第二種方式是使用完成工作的接續,或使用 C# 和 Visual Basic 的異步語言功能,以異步方式等候完成工作。 您提供給 Task.ContinueWith 方法的委派會使用一個代表先行任務的 Task 物件。 在 Completion 屬性的情況下,接續的委派會接受完成的任務本身。 下列範例與上一個範例類似,不同之處在於它也會使用 ContinueWith 方法來建立接續工作,以列印整體數據流作業的狀態。
// Create an ActionBlock<int> object that prints its input
// and throws ArgumentOutOfRangeException if the input
// is less than zero.
var throwIfNegative = new ActionBlock<int>(n =>
{
Console.WriteLine($"n = {n}");
if (n < 0)
{
throw new ArgumentOutOfRangeException();
}
});
// Create a continuation task that prints the overall
// task status to the console when the block finishes.
throwIfNegative.Completion.ContinueWith(task =>
{
Console.WriteLine($"The status of the completion task is '{task.Status}'.");
});
// Post values to the block.
throwIfNegative.Post(0);
throwIfNegative.Post(-1);
throwIfNegative.Post(1);
throwIfNegative.Post(-2);
throwIfNegative.Complete();
// Wait for completion in a try/catch block.
try
{
throwIfNegative.Completion.Wait();
}
catch (AggregateException ae)
{
// If an unhandled exception occurs during dataflow processing, all
// exceptions are propagated through an AggregateException object.
ae.Handle(e =>
{
Console.WriteLine($"Encountered {e.GetType().Name}: {e.Message}");
return true;
});
}
/* Output:
n = 0
n = -1
The status of the completion task is 'Faulted'.
Encountered ArgumentOutOfRangeException: Specified argument was out of the range
of valid values.
*/
' Create an ActionBlock<int> object that prints its input
' and throws ArgumentOutOfRangeException if the input
' is less than zero.
Dim throwIfNegative = New ActionBlock(Of Integer)(Sub(n)
Console.WriteLine("n = {0}", n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Sub)
' Create a continuation task that prints the overall
' task status to the console when the block finishes.
throwIfNegative.Completion.ContinueWith(Sub(task) Console.WriteLine("The status of the completion task is '{0}'.", task.Status))
' Post values to the block.
throwIfNegative.Post(0)
throwIfNegative.Post(-1)
throwIfNegative.Post(1)
throwIfNegative.Post(-2)
throwIfNegative.Complete()
' Wait for completion in a try/catch block.
Try
throwIfNegative.Completion.Wait()
Catch ae As AggregateException
' If an unhandled exception occurs during dataflow processing, all
' exceptions are propagated through an AggregateException object.
ae.Handle(Function(e)
Console.WriteLine("Encountered {0}: {1}", e.GetType().Name, e.Message)
Return True
End Function)
End Try
' Output:
' n = 0
' n = -1
' The status of the completion task is 'Faulted'.
' Encountered ArgumentOutOfRangeException: Specified argument was out of the range
' of valid values.
'
您也可以使用接續工作主體中的屬性 IsCanceled 來判斷數據流區塊完成狀態的其他資訊。 如需了解更多關於接續工作及其與取消和錯誤處理的相關資訊,請參閱使用接續工作進行串聯、工作取消和例外狀況處理。
預先定義的數據流區塊類型
TPL 數據流連結庫提供數個預先定義的數據流區塊類型。 這些類型分為三個類別: 緩衝區塊、 執行區塊和 群組區塊。 下列各節說明組成這些類別的區塊類型。
緩衝區塊
緩衝區塊會保存數據供數據取用者使用。 TPL 資料流連結庫提供三種緩衝區塊類型: System.Threading.Tasks.Dataflow.BufferBlock<T>、 System.Threading.Tasks.Dataflow.BroadcastBlock<T>和 System.Threading.Tasks.Dataflow.WriteOnceBlock<T>。
BufferBlock<T>
類別 BufferBlock<T> 代表一般用途異步傳訊結構。 這個類別儲存的是一個先入先出(FIFO)佇列,可以由多個來源寫入訊息,或被多個目標讀取。 當目標從 BufferBlock<T> 物件接收訊息時,該訊息會從消息佇列中移除。 因此,雖然 BufferBlock<T> 物件可以有多個目標,但只有一個目標會收到每個訊息。 當您想要將多個訊息傳遞至另一個元件,而且該元件必須接收每個訊息時,類別 BufferBlock<T> 會很有用。
下列基本範例會將多個 Int32 值傳送到 BufferBlock<T> 物件,然後從該物件讀取這些值。
// Create a BufferBlock<int> object.
var bufferBlock = new BufferBlock<int>();
// Post several messages to the block.
for (int i = 0; i < 3; i++)
{
bufferBlock.Post(i);
}
// Receive the messages back from the block.
for (int i = 0; i < 3; i++)
{
Console.WriteLine(bufferBlock.Receive());
}
/* Output:
0
1
2
*/
' Create a BufferBlock<int> object.
Dim bufferBlock = New BufferBlock(Of Integer)()
' Post several messages to the block.
For i As Integer = 0 To 2
bufferBlock.Post(i)
Next i
' Receive the messages back from the block.
For i As Integer = 0 To 2
Console.WriteLine(bufferBlock.Receive())
Next i
' Output:
' 0
' 1
' 2
'
如需示範如何將訊息寫入物件及讀取 BufferBlock<T> 訊息的完整範例,請參閱 如何:將訊息寫入數據流區塊和讀取訊息。
BroadcastBlock<T>
當您必須將多個訊息傳遞至另一個元件,但該元件只需要最新的值時,類別 BroadcastBlock<T> 就很有用。 當您想要將訊息廣播至多個元件時,這個類別也很有用。
下列基本範例會將 Double 值張貼至 BroadcastBlock<T> 對象,然後從該物件讀取該值數次。 由於值在讀取物件後不會從物件中 BroadcastBlock<T> 移除,因此每次都可以使用相同的值。
// Create a BroadcastBlock<double> object.
var broadcastBlock = new BroadcastBlock<double>(null);
// Post a message to the block.
broadcastBlock.Post(Math.PI);
// Receive the messages back from the block several times.
for (int i = 0; i < 3; i++)
{
Console.WriteLine(broadcastBlock.Receive());
}
/* Output:
3.14159265358979
3.14159265358979
3.14159265358979
*/
' Create a BroadcastBlock<double> object.
Dim broadcastBlock = New BroadcastBlock(Of Double)(Nothing)
' Post a message to the block.
broadcastBlock.Post(Math.PI)
' Receive the messages back from the block several times.
For i As Integer = 0 To 2
Console.WriteLine(broadcastBlock.Receive())
Next i
' Output:
' 3.14159265358979
' 3.14159265358979
' 3.14159265358979
'
如需示範如何使用 BroadcastBlock<T> 將訊息廣播至多個目標區塊的完整範例,請參閱 如何:在數據流區塊中指定工作排程器。
寫入一次區塊<T>
類別 WriteOnceBlock<T> 類似於 BroadcastBlock<T> 類別,不同之處在於 WriteOnceBlock<T> 物件只能寫入一次。 您可以將它視為WriteOnceBlock<T>類似 C# 的readonly(在 Visual Basic 中為ReadOnly)關鍵詞,不同之處在於WriteOnceBlock<T>物件在接收到值後變得不可變,而不是在建構時。 與類別一樣 BroadcastBlock<T> ,當目標從物件接收訊息 WriteOnceBlock<T> 時,不會從該物件中移除該訊息。 因此,多個目標會收到訊息的複本。 當您只想傳播多個訊息的第一個時,類別 WriteOnceBlock<T> 會很有用。
下列基本範例會將多個 String 值張貼至 WriteOnceBlock<T> 對象,然後從該物件讀取值。 WriteOnceBlock<T>因為物件只能寫入一次,所以物件收到訊息之後WriteOnceBlock<T>,就會捨棄後續的訊息。
// Create a WriteOnceBlock<string> object.
var writeOnceBlock = new WriteOnceBlock<string>(null);
// Post several messages to the block in parallel. The first
// message to be received is written to the block.
// Subsequent messages are discarded.
Parallel.Invoke(
() => writeOnceBlock.Post("Message 1"),
() => writeOnceBlock.Post("Message 2"),
() => writeOnceBlock.Post("Message 3"));
// Receive the message from the block.
Console.WriteLine(writeOnceBlock.Receive());
/* Sample output:
Message 2
*/
' Create a WriteOnceBlock<string> object.
Dim writeOnceBlock = New WriteOnceBlock(Of String)(Nothing)
' Post several messages to the block in parallel. The first
' message to be received is written to the block.
' Subsequent messages are discarded.
Parallel.Invoke(Function() writeOnceBlock.Post("Message 1"), Function() writeOnceBlock.Post("Message 2"), Function() writeOnceBlock.Post("Message 3"))
' Receive the message from the block.
Console.WriteLine(writeOnceBlock.Receive())
' Sample output:
' Message 2
'
如需示範如何使用 WriteOnceBlock<T> 來接收第一個完成作業的值的完整範例,請參閱 如何:取消鏈接數據流區塊。
執行區塊
執行區塊會針對每個收到的數據片段呼叫使用者提供的委派。 TPL 資料流連結庫提供三種執行區塊類型: ActionBlock<TInput>、 System.Threading.Tasks.Dataflow.TransformBlock<TInput,TOutput>和 System.Threading.Tasks.Dataflow.TransformManyBlock<TInput,TOutput>。
行動方塊<T>
類別 ActionBlock<TInput> 是一個目標區塊,會在接收數據時呼叫委派。 將 ActionBlock<TInput> 對象視為當數據可供使用時以異步方式執行的委派。 您提供給 ActionBlock<TInput> 物件的委派可以是 類型 Action<T> 或型別 System.Func<TInput, Task>。 當您使用 ActionBlock<TInput> 物件搭配 Action<T> 時,當委派傳回,會將每個輸入元素的處理視為已完成。 當您使用 ActionBlock<TInput> 物件搭配 System.Func<TInput, Task> 時,只有在傳回的 Task 物件完成後,才會視每個輸入元素的處理為已完成。 藉由使用這兩種機制,您可以針對每個輸入元素的同步和異步處理使用 ActionBlock<TInput> 。
下列基本範例會將多個 Int32 值張貼至 ActionBlock<TInput> 物件。 物件會將 ActionBlock<TInput> 這些值列印到主控台。 本範例接著會將 區塊設定為已完成狀態,並等候所有數據流工作完成。
// Create an ActionBlock<int> object that prints values
// to the console.
var actionBlock = new ActionBlock<int>(n => Console.WriteLine(n));
// Post several messages to the block.
for (int i = 0; i < 3; i++)
{
actionBlock.Post(i * 10);
}
// Set the block to the completed state and wait for all
// tasks to finish.
actionBlock.Complete();
actionBlock.Completion.Wait();
/* Output:
0
10
20
*/
' Create an ActionBlock<int> object that prints values
' to the console.
Dim actionBlock = New ActionBlock(Of Integer)(Function(n) WriteLine(n))
' Post several messages to the block.
For i As Integer = 0 To 2
actionBlock.Post(i * 10)
Next i
' Set the block to the completed state and wait for all
' tasks to finish.
actionBlock.Complete()
actionBlock.Completion.Wait()
' Output:
' 0
' 10
' 20
'
如需示範如何搭配 ActionBlock<TInput> 類別使用委派的完整範例,請參閱 如何:在數據流區塊接收數據時執行動作。
TransformBlock<TInput、TOutput>
類別 TransformBlock<TInput,TOutput> 與 類別類似 ActionBlock<TInput> ,不同之處在於它同時做為來源和目標。 您傳遞至 TransformBlock<TInput,TOutput> 物件的委派會返回一個TOutput類型的值。 您提供給 TransformBlock<TInput,TOutput> 物件的委派可以是型別 System.Func<TInput, TOutput> 或型別 System.Func<TInput, Task<TOutput>>。 當您使用帶有 TransformBlock<TInput,TOutput> 的 System.Func<TInput, TOutput> 物件時,當委派傳回時,每個輸入元素的處理即視為已完成。 當您使用與TransformBlock<TInput,TOutput> 搭配的 System.Func<TInput, Task<TOutput>> 物件時,只有在傳回的 Task<TResult> 物件完成時,才會將每個輸入元素的處理視為已完成。 和 ActionBlock<TInput> 一樣,通過使用這兩種機制,您可以針對每個輸入元素的同步和異步處理使用 TransformBlock<TInput,TOutput>。
下列基本範例會建立 物件 TransformBlock<TInput,TOutput> ,以計算其輸入的平方根。 物件會 TransformBlock<TInput,TOutput> 接受 Int32 值做為輸入,併產生 Double 值做為輸出。
// Create a TransformBlock<int, double> object that
// computes the square root of its input.
var transformBlock = new TransformBlock<int, double>(n => Math.Sqrt(n));
// Post several messages to the block.
transformBlock.Post(10);
transformBlock.Post(20);
transformBlock.Post(30);
// Read the output messages from the block.
for (int i = 0; i < 3; i++)
{
Console.WriteLine(transformBlock.Receive());
}
/* Output:
3.16227766016838
4.47213595499958
5.47722557505166
*/
' Create a TransformBlock<int, double> object that
' computes the square root of its input.
Dim transformBlock = New TransformBlock(Of Integer, Double)(Function(n) Math.Sqrt(n))
' Post several messages to the block.
transformBlock.Post(10)
transformBlock.Post(20)
transformBlock.Post(30)
' Read the output messages from the block.
For i As Integer = 0 To 2
Console.WriteLine(transformBlock.Receive())
Next i
' Output:
' 3.16227766016838
' 4.47213595499958
' 5.47722557505166
'
如需在 Windows Forms 應用程式中執行影像處理之數據流區塊網路中使用 TransformBlock<TInput,TOutput> 的完整範例,請參閱 逐步解說:在 Windows Forms 應用程式中使用數據流。
TransformManyBlock<TInput、TOutput>
類別 TransformManyBlock<TInput,TOutput> 類似於 TransformBlock<TInput,TOutput> 類別,不同之處在於為每個 TransformManyBlock<TInput,TOutput> 輸入值產生零個或多個輸出值,而不是每個輸入值的一個輸出值。 您提供給 TransformManyBlock<TInput,TOutput> 物件的委派可以是型別 System.Func<TInput, IEnumerable<TOutput>> 或型別 System.Func<TInput, Task<IEnumerable<TOutput>>>。 當您使用帶有 TransformManyBlock<TInput,TOutput> 的 System.Func<TInput, IEnumerable<TOutput>> 物件時,當委派傳回時,每個輸入元素的處理即視為已完成。 當您使用 TransformManyBlock<TInput,TOutput> 物件搭配 System.Func<TInput, Task<IEnumerable<TOutput>>> 時,只有在回傳的 System.Threading.Tasks.Task<IEnumerable<TOutput>> 物件完成後,才會將每個輸入元件的處理視為完成。
下列基本範例會創建一個TransformManyBlock<TInput,TOutput>物件,該物件將字串分割成字元組成的序列。 物件會 TransformManyBlock<TInput,TOutput> 接受 String 值做為輸入,併產生 Char 值做為輸出。
// Create a TransformManyBlock<string, char> object that splits
// a string into its individual characters.
var transformManyBlock = new TransformManyBlock<string, char>(
s => s.ToCharArray());
// Post two messages to the first block.
transformManyBlock.Post("Hello");
transformManyBlock.Post("World");
// Receive all output values from the block.
for (int i = 0; i < ("Hello" + "World").Length; i++)
{
Console.WriteLine(transformManyBlock.Receive());
}
/* Output:
H
e
l
l
o
W
o
r
l
d
*/
' Create a TransformManyBlock<string, char> object that splits
' a string into its individual characters.
Dim transformManyBlock = New TransformManyBlock(Of String, Char)(Function(s) s.ToCharArray())
' Post two messages to the first block.
transformManyBlock.Post("Hello")
transformManyBlock.Post("World")
' Receive all output values from the block.
For i As Integer = 0 To ("Hello" & "World").Length - 1
Console.WriteLine(transformManyBlock.Receive())
Next i
' Output:
' H
' e
' l
' l
' o
' W
' o
' r
' l
' d
'
如需針對 TransformManyBlock<TInput,TOutput> 數據流管線中每個輸入產生多個獨立輸出的完整範例,請參閱 逐步解說:建立數據流管線。
並行度
每個 ActionBlock<TInput>、 TransformBlock<TInput,TOutput>和 TransformManyBlock<TInput,TOutput> 物件都會緩衝輸入訊息,直到區塊準備好處理它們為止。 根據預設,這些類別會依照接收訊息的順序來處理訊息,一次一則訊息。 您也可以指定平行處理原則的程度,讓 ActionBlock<TInput>和 TransformBlock<TInput,TOutput>TransformManyBlock<TInput,TOutput> 對象同時處理多個訊息。 如需並行執行的詳細資訊,請參閱本檔稍後指定平行處理原則的程度一節。 如需將平行處理原則的程度設定為讓執行數據流區塊一次處理多個訊息的範例,請參閱 如何:在數據流區塊中指定平行處理原則的程度。
委派類型的摘要
下表摘要說明您可以提供給 ActionBlock<TInput>、 TransformBlock<TInput,TOutput>和 TransformManyBlock<TInput,TOutput> 物件的委派類型。 此數據表也會指定委派類型會以同步或異步方式運作。
| 類型 | 同步委派類型 | 異步代理類型 |
|---|---|---|
| ActionBlock<TInput> | System.Action |
System.Func<TInput, Task> |
| TransformBlock<TInput,TOutput> | System.Func<TInput, TOutput> |
System.Func<TInput, Task<TOutput>> |
| TransformManyBlock<TInput,TOutput> | System.Func<TInput, IEnumerable<TOutput>> |
System.Func<TInput, Task<IEnumerable<TOutput>>> |
當您使用執行區塊類型時,也可以使用 Lambda 表達式。 如需示範如何搭配執行區塊使用 Lambda 表達式的範例,請參閱 如何:在數據流區塊接收數據時執行動作。
群組區塊
群組區塊會結合來自一或多個來源的數據,並在各種條件約束下合併。 TPL 資料流連結庫提供三種聯結區塊類型: BatchBlock<T>、 JoinBlock<T1,T2>和 BatchedJoinBlock<T1,T2>。
批次區塊<T>
類別 BatchBlock<T> 會將一組稱為批次的輸入數據結合成輸出數據的陣列。 當您建立 BatchBlock<T> 物件時,您可以指定每個批次的大小。 BatchBlock<T>當物件收到指定的輸入項目計數時,它會以異步方式傳播包含這些元素的陣列。 如果物件 BatchBlock<T> 設定為已完成狀態,但不包含足夠的元素來形成批次,它會傳播包含其餘輸入元素的最終陣列。
類別 BatchBlock<T> 會以 貪婪 或 非貪婪 模式運作。 在預設的貪婪模式中,BatchBlock<T> 物件會接受其被提供的每個訊息,並在收到指定的元素數量後,以陣列的形式傳出。 在非貪婪模式中, BatchBlock<T> 物件會延後所有傳入訊息,直到有足夠的來源提供訊息給區塊以形成批次為止。 貪婪模式的執行效能通常比非貪婪模式更好,因為它需要較少的處理額外負荷。 不過,當您必須以整體方式協調多個來源的耗用量時,您可以使用非貪婪模式。 透過在Greedy建構函式的False參數中將dataflowBlockOptions設為BatchBlock<T>來指定非貪婪模式。
下列基本範例會將多個 Int32 值傳送至批次中儲存十個元素的 BatchBlock<T> 物件。 為了保證所有值都會傳播出 BatchBlock<T>,這個範例會呼叫 Complete 方法。 方法會將 CompleteBatchBlock<T> 物件設定為已完成的狀態,因此物件 BatchBlock<T> 會將任何剩餘的元素傳播為最終批次。
// Create a BatchBlock<int> object that holds ten
// elements per batch.
var batchBlock = new BatchBlock<int>(10);
// Post several values to the block.
for (int i = 0; i < 13; i++)
{
batchBlock.Post(i);
}
// Set the block to the completed state. This causes
// the block to propagate out any remaining
// values as a final batch.
batchBlock.Complete();
// Print the sum of both batches.
Console.WriteLine($"The sum of the elements in batch 1 is {batchBlock.Receive().Sum()}.");
Console.WriteLine($"The sum of the elements in batch 2 is {batchBlock.Receive().Sum()}.");
/* Output:
The sum of the elements in batch 1 is 45.
The sum of the elements in batch 2 is 33.
*/
' Create a BatchBlock<int> object that holds ten
' elements per batch.
Dim batchBlock = New BatchBlock(Of Integer)(10)
' Post several values to the block.
For i As Integer = 0 To 12
batchBlock.Post(i)
Next i
' Set the block to the completed state. This causes
' the block to propagate out any remaining
' values as a final batch.
batchBlock.Complete()
' Print the sum of both batches.
Console.WriteLine("The sum of the elements in batch 1 is {0}.", batchBlock.Receive().Sum())
Console.WriteLine("The sum of the elements in batch 2 is {0}.", batchBlock.Receive().Sum())
' Output:
' The sum of the elements in batch 1 is 45.
' The sum of the elements in batch 2 is 33.
'
如需用來 BatchBlock<T> 改善資料庫插入作業效率的完整範例,請參閱 逐步解說:使用 BatchBlock 和 BatchedJoinBlock 以改善效率。
JoinBlock<T1、T2、...>
JoinBlock<T1,T2>和 JoinBlock<T1,T2,T3> 類別會收集輸入元素,並傳播出System.Tuple<T1,T2>或System.Tuple<T1,T2,T3>包含這些項目的物件。 JoinBlock<T1,T2> 類別和 JoinBlock<T1,T2,T3> 類別不會繼承自 ITargetBlock<TInput>。 相反地,它們會提供實作 Target1的屬性、Target2、 Target3和 ITargetBlock<TInput>。
像 BatchBlock<T> 一樣,JoinBlock<T1,T2> 和 JoinBlock<T1,T2,T3> 都可以在貪婪或非貪婪模式中運作。 在預設的貪婪模式下,JoinBlock<T1,T2> 或 JoinBlock<T1,T2,T3> 物件會接受所有提供給它的訊息,並且僅在所有目標至少收到一則訊息後才傳播出元組。 在非貪婪模式中,JoinBlock<T1,T2> 或 JoinBlock<T1,T2,T3> 物件會延後所有傳入訊息,直到所有目標都被提供了建立 tuple 所需的數據。 此時,區塊會參與兩階段認可協定,以原子方式從來源擷取所有必要的項目。 此延後可讓另一個實體同時取用數據,讓整體系統能夠向前推進。
下列基本範例示範 JoinBlock<T1,T2,T3> 物件需要多個數據來計算值的情況。 這個範例建立一個 JoinBlock<T1,T2,T3> 物件,該物件需要兩個 Int32 值和一個 Char 值來執行算術運算。
// Create a JoinBlock<int, int, char> object that requires
// two numbers and an operator.
var joinBlock = new JoinBlock<int, int, char>();
// Post two values to each target of the join.
joinBlock.Target1.Post(3);
joinBlock.Target1.Post(6);
joinBlock.Target2.Post(5);
joinBlock.Target2.Post(4);
joinBlock.Target3.Post('+');
joinBlock.Target3.Post('-');
// Receive each group of values and apply the operator part
// to the number parts.
for (int i = 0; i < 2; i++)
{
var data = joinBlock.Receive();
switch (data.Item3)
{
case '+':
Console.WriteLine($"{data.Item1} + {data.Item2} = {data.Item1 + data.Item2}");
break;
case '-':
Console.WriteLine($"{data.Item1} - {data.Item2} = {data.Item1 - data.Item2}");
break;
default:
Console.WriteLine($"Unknown operator '{data.Item3}'.");
break;
}
}
/* Output:
3 + 5 = 8
6 - 4 = 2
*/
' Create a JoinBlock<int, int, char> object that requires
' two numbers and an operator.
Dim joinBlock = New JoinBlock(Of Integer, Integer, Char)()
' Post two values to each target of the join.
joinBlock.Target1.Post(3)
joinBlock.Target1.Post(6)
joinBlock.Target2.Post(5)
joinBlock.Target2.Post(4)
joinBlock.Target3.Post("+"c)
joinBlock.Target3.Post("-"c)
' Receive each group of values and apply the operator part
' to the number parts.
For i As Integer = 0 To 1
Dim data = joinBlock.Receive()
Select Case data.Item3
Case "+"c
Console.WriteLine("{0} + {1} = {2}", data.Item1, data.Item2, data.Item1 + data.Item2)
Case "-"c
Console.WriteLine("{0} - {1} = {2}", data.Item1, data.Item2, data.Item1 - data.Item2)
Case Else
Console.WriteLine("Unknown operator '{0}'.", data.Item3)
End Select
Next i
' Output:
' 3 + 5 = 8
' 6 - 4 = 2
'
如需使用 JoinBlock<T1,T2> 非貪婪模式中物件合作共用資源的完整範例,請參閱 如何:使用 JoinBlock 從多個來源讀取數據。
BatchedJoinBlock<T1、T2、...>
BatchedJoinBlock<T1,T2>和 BatchedJoinBlock<T1,T2,T3> 類別會收集輸入元素的批次,並傳播出System.Tuple(IList(T1), IList(T2))或System.Tuple(IList(T1), IList(T2), IList(T3))包含這些元素的物件。 將BatchedJoinBlock<T1,T2>視為BatchBlock<T>和JoinBlock<T1,T2>的組合。 當您建立 物件時,請指定 BatchedJoinBlock<T1,T2> 每個批次的大小。
BatchedJoinBlock<T1,T2>也提供 實作 Target1的屬性 Target2 和 ITargetBlock<TInput>。 當接收到來自所有目標的指定數量的輸入元素時,BatchedJoinBlock<T1,T2> 物件會以非同步方式傳播出包含這些元素的 System.Tuple(IList(T1), IList(T2)) 物件。
下列基本範例會建立一個BatchedJoinBlock<T1,T2>物件,用於保存結果、Int32值,以及作為Exception物件的錯誤。 這個範例會執行多個作業,並將結果 Target1 寫入 屬性,並將錯誤 Target2 寫入 對象的屬性 BatchedJoinBlock<T1,T2> 。 由於成功和失敗的作業計數事先未知,因此 IList<T> 物件可讓每個目標接收零個或多個值。
// For demonstration, create a Func<int, int> that
// returns its argument, or throws ArgumentOutOfRangeException
// if the argument is less than zero.
Func<int, int> DoWork = n =>
{
if (n < 0)
throw new ArgumentOutOfRangeException();
return n;
};
// Create a BatchedJoinBlock<int, Exception> object that holds
// seven elements per batch.
var batchedJoinBlock = new BatchedJoinBlock<int, Exception>(7);
// Post several items to the block.
foreach (int i in new int[] { 5, 6, -7, -22, 13, 55, 0 })
{
try
{
// Post the result of the worker to the
// first target of the block.
batchedJoinBlock.Target1.Post(DoWork(i));
}
catch (ArgumentOutOfRangeException e)
{
// If an error occurred, post the Exception to the
// second target of the block.
batchedJoinBlock.Target2.Post(e);
}
}
// Read the results from the block.
var results = batchedJoinBlock.Receive();
// Print the results to the console.
// Print the results.
foreach (int n in results.Item1)
{
Console.WriteLine(n);
}
// Print failures.
foreach (Exception e in results.Item2)
{
Console.WriteLine(e.Message);
}
/* Output:
5
6
13
55
0
Specified argument was out of the range of valid values.
Specified argument was out of the range of valid values.
*/
' For demonstration, create a Func<int, int> that
' returns its argument, or throws ArgumentOutOfRangeException
' if the argument is less than zero.
Dim DoWork As Func(Of Integer, Integer) = Function(n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
Return n
End Function
' Create a BatchedJoinBlock<int, Exception> object that holds
' seven elements per batch.
Dim batchedJoinBlock = New BatchedJoinBlock(Of Integer, Exception)(7)
' Post several items to the block.
For Each i As Integer In New Integer() {5, 6, -7, -22, 13, 55, 0}
Try
' Post the result of the worker to the
' first target of the block.
batchedJoinBlock.Target1.Post(DoWork(i))
Catch e As ArgumentOutOfRangeException
' If an error occurred, post the Exception to the
' second target of the block.
batchedJoinBlock.Target2.Post(e)
End Try
Next i
' Read the results from the block.
Dim results = batchedJoinBlock.Receive()
' Print the results to the console.
' Print the results.
For Each n As Integer In results.Item1
Console.WriteLine(n)
Next n
' Print failures.
For Each e As Exception In results.Item2
Console.WriteLine(e.Message)
Next e
' Output:
' 5
' 6
' 13
' 55
' 0
' Specified argument was out of the range of valid values.
' Specified argument was out of the range of valid values.
'
如需一個使用 BatchedJoinBlock<T1,T2> 擷取程式從資料庫讀取時結果及任何例外的完整範例,請參閱 逐步解說:使用 BatchBlock 和 BatchedJoinBlock 以改善效率。
設定數據流區塊行為
您可以將物件提供給 System.Threading.Tasks.Dataflow.DataflowBlockOptions 數據流區塊類型的建構函式,以啟用其他選項。 這些選項控制一些行為,例如管理基礎工作的排程器和平行處理的程度。 DataflowBlockOptions也具有衍生型別,可指定特定數據流區塊類型特有的行為。 下表摘要說明哪些選項類型與每個數據流區塊類型相關聯。
下列各節提供有關重要數據流區塊選項類型的其他資訊,這些選項類型可透過 System.Threading.Tasks.Dataflow.DataflowBlockOptions、System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions 和 System.Threading.Tasks.Dataflow.GroupingDataflowBlockOptions 類別取得。
指定工作排程器
每個預先定義的數據流區塊都會使用 TPL 工作排程機制來執行活動,例如將數據傳播至目標、從來源接收數據,以及在數據可用時執行使用者定義的委派。 TaskScheduler 是抽象類,表示將工作排入線程佇列的工作排程器。 預設工作排程器Default使用ThreadPool類別來排入佇列並執行工作。 您可以藉由在建構數據流區塊物件時,設定TaskScheduler屬性來覆寫預設的工作排程器。
當相同的工作排程器管理多個數據流區塊時,它可以跨這些區塊強制執行原則。 例如,如果每個數據流區塊都設定為以相同 ConcurrentExclusiveSchedulerPair 對象的獨佔排程器為目標,則會串行化跨這些區塊執行的所有工作。 同樣地,如果這些區塊設定為以相同 ConcurrentExclusiveSchedulerPair 物件的並行排程器為目標,且該排程器設定為具有最大並行層級,則來自這些區塊的所有工作都會限制為該數目的並行作業。 如需使用 ConcurrentExclusiveSchedulerPair 類別以平行方式執行讀取作業,但寫入作業需獨立於所有其他作業進行的範例,請參閱 如何:在數據流區塊中指定工作排程器。 如需 TPL 中工作排程器的詳細資訊,請參閱 TaskScheduler 類別主題。
指定平行度的程度
根據預設,TPL 數據流連結庫提供的三個執行區塊類型、 ActionBlock<TInput>、 TransformBlock<TInput,TOutput>和 TransformManyBlock<TInput,TOutput>一次處理一則訊息。 這些數據流區塊類型也會依照接收訊息的順序來處理訊息。 若要讓這些數據流區塊同時處理訊息,請在建構數據流區塊物件時設定 ExecutionDataflowBlockOptions.MaxDegreeOfParallelism 屬性。
的預設值 MaxDegreeOfParallelism 為 1,可保證數據流區塊一次處理一則訊息。 將此屬性設定為大於1的值,可讓資料流區塊同時處理多個訊息。 將此屬性設定為 DataflowBlockOptions.Unbounded 可讓基礎工作排程器管理並行程度上限。
這很重要
當您指定大於 1 的最大平行處理原則程度時,會同時處理多個訊息,因此可能不會依照接收訊息的順序處理訊息。 不過,從區塊輸出訊息的順序與接收訊息的順序相同。
因為 MaxDegreeOfParallelism 屬性代表平行處理原則的最大程度,因此數據流區塊的執行方式可能會比您指定的平行處理原則少一些。 數據流區塊可能會使用較少程度的平行處理原則來符合其功能需求,或因為缺少可用的系統資源。 數據流區塊永遠不會選擇超過您指定的並行性。
屬性的值 MaxDegreeOfParallelism 是每個數據流區塊對象的獨佔。 例如,如果四個數據流區塊物件分別針對平行處理原則的最大程度指定 1,則這四個數據流區塊物件都可能會以平行方式執行。
如需設定平行處理原則的最大程度以平行方式執行長時間作業的範例,請參閱 如何:在數據流區塊中指定平行處理原則的程度。
指定每個工作的訊息數目
預先定義的數據流區塊類型會使用工作來處理多個輸入元素。 這有助於將處理數據所需的工作物件數目降至最低,讓應用程式更有效率地執行。 不過,當來自一組數據流區塊的工作正在處理數據時,來自其他數據流區塊的工作可能需要等候佇列訊息來處理時間。 若要在數據流工作之間提供更好的公平性,請設定 MaxMessagesPerTask 屬性。 當 MaxMessagesPerTask 設定為 DataflowBlockOptions.Unbounded時,這是預設值,數據流區塊所使用的工作會處理可用的訊息數量。 當 MaxMessagesPerTask 設定為Unbounded以外的值時,數據流區塊最多會處理每個Task物件的訊息數量。 雖然設定 MaxMessagesPerTask 屬性會增加工作之間的公平性,但它可能會導致系統建立比必要更多的工作,這樣可能會降低效能。
啟用取消
TPL 提供一種機制,可讓工作以合作方式協調取消。 若要讓數據流區塊參與此取消機制,請設定 CancellationToken 屬性。 當此 CancellationToken 物件設定為已取消狀態時,監視此權杖的所有資料流程區塊都會完成其目前專案的執行,但不會開始處理後續專案。 這些數據流區塊也會清除任何緩衝訊息、釋放任何來源和目標區塊的連接,以及轉換為已取消的狀態。 藉由轉換至已取消的狀態, Completion 屬性會將 Status 屬性設定為 Canceled,除非處理期間發生例外狀況。 在這裡情況下, Status 會設定為 Faulted。
如需示範如何在 Windows Forms 應用程式中使用取消的範例,請參閱 如何:取消數據流區塊。 如需 TPL 中取消的詳細資訊,請參閱 工作取消。
指定貪婪與非貪婪行為
數個分組數據流區塊類型可以在 貪婪 或 非貪婪 模式中運作。 根據預設,預先定義的數據流區塊類型會以貪婪模式運作。
對於聯結區塊類型 (例如 JoinBlock<T1,T2>),貪婪模式表示即使要聯結的對應資料尚不可用,區塊也會立即接受資料。 非貪婪模式表示區塊會暫時阻止所有傳入的訊息,直到每個目標端都有一條訊息可用來完成聯結。 如果不再提供任何延後訊息,聯結區塊會釋放所有延後的郵件,並重新啟動進程。 BatchBlock<T>對於 類別而言,貪婪和非貪婪行為很類似,不同之處在於,在非貪婪模式下,物件會延後所有傳入訊息,BatchBlock<T>直到從不同來源取得足夠的訊息,才能完成批次。
若要指定資料串流區塊的非貪婪模式,請將 設定 Greedy 為 False。 如需示範如何使用非貪婪模式讓多個聯結區塊更有效率地共用數據源的範例,請參閱 如何:使用 JoinBlock 從多個來源讀取數據。
自訂數據流區塊
雖然 TPL 數據流連結庫提供許多預先定義的區塊類型,但您可以建立執行自定義行為的其他區塊類型。 直接實作 ISourceBlock<TOutput> 或 介面,或使用 ITargetBlock<TInput>Encapsulate 方法來建置複雜區塊,以封裝現有區塊類型的行為。 如需示範如何實作自定義數據流區塊功能的範例,請參閱 逐步解說:建立自定義數據流區塊類型。
[相關主題]
| 標題 | 說明 |
|---|---|
| 如何:將訊息寫入數據流區塊並從數據流區塊讀取訊息 | 示範如何將訊息寫入物件,以及從 BufferBlock<T> 物件讀取訊息。 |
| 如何:實作 Producer-Consumer 數據流模式 | 描述如何使用數據流模型來實作生產者-取用者模式,其中生產者會將訊息傳送至數據流區塊,而取用者會從該區塊讀取訊息。 |
| 如何:在數據流區塊接收數據時執行動作 | 描述如何為執行資料流區塊類型提供委派:ActionBlock<TInput>、TransformBlock<TInput,TOutput> 和 TransformManyBlock<TInput,TOutput>。 |
| 逐步解說:建立數據流管線 | 描述如何建立數據流管線,以從 Web 下載文字,並在該文字上執行作業。 |
| 如何:取消鏈接數據流區塊 | 示範如何在來源提供訊息給目標之後,使用 LinkTo 方法將目標區塊與其來源取消連結。 |
| 逐步解說:在 Windows Forms 應用程式中使用數據流 | 示範如何建立數據流區塊網路,以在 Windows Forms 應用程式中執行影像處理。 |
| 如何:取消數據流區塊 | 示範如何在 Windows Forms 應用程式中使用取消。 |
| 如何:使用 JoinBlock 從多個來源讀取數據 | 說明如何在從多個來源取得數據時使用 JoinBlock<T1,T2> 類別來執行作業,以及如何使用非窮盡模式讓多個聯結區塊更有效率地共用數據源。 |
| 如何:在數據流區塊中指定平行處理原則的程度 | 描述如何設定 MaxDegreeOfParallelism 屬性,讓執行數據流區塊一次處理多個訊息。 |
| 如何:在數據流區塊中指定工作排程器 | 示範如何在應用程式中使用數據流時關聯特定的工作排程器。 |
| 逐步解說:使用 BatchBlock 和 BatchedJoinBlock 以改善效率 | 描述如何使用 BatchBlock<T> 類別來改善資料庫插入作業的效率,以及如何使用 BatchedJoinBlock<T1,T2> 類別來擷取程式從資料庫讀取時發生的結果和任何例外狀況。 |
| 逐步解說:建立自定義數據流區塊類型 | 示範兩種方式來建立實作自定義行為的數據流區塊類型。 |
| 工作平行程式庫 (TPL) | 介紹 TPL,這是一個連結庫,可簡化 .NET Framework 應用程式中的平行和並行程序設計。 |