工作平行處理原則 (並行執行階段)
更新:2011 年 3 月
本文件說明並行執行階段中工作和工作群組的角色。 當您擁有兩個以上要同時執行的獨立工作項目時,請使用工作群組。 例如,假設您有個將剩餘工作分成兩塊的遞迴演算法。 您可以使用工作群組,同時執行這些分割部分。 相反地,當您要將相同常式平行套用至集合的每個項目時,請使用平行演算法,例如 Concurrency::parallel_for。 如需平行演算法的詳細資訊,請參閱平行演算法。
工作和工作群組
「工作」(Task) 是執行特定工作的工作單位。 工作通常可以和其他工作平行處理,也可以分解為更多更精細的工作。 「工作群組」(Task Group) 會組織工作集合。 工作群組會將工作推入至工作竊取佇列。 排程器會將工作從這個佇列中移除,並使用可用的運算資源執行這些工作。 將工作加入至工作群組之後,您可以等候所有工作完成或取消尚未開始的工作。
PPL 以 Concurrency::task_group 和 Concurrency::structured_task_group 類別表示工作群組,並以 Concurrency::task_handle 類別表示工作。 task_handle 類別會封裝執行工作的程式碼。 此程式碼採用 Lambda 函式、函式指標或函式物件的形式,通常稱為「工作函式」(Work Function)。 您通常不必直接使用 task_handle 物件, 而是將工作函式傳遞給工作群組,而工作群組就會建立及管理 task_handle 物件。
PPL 將工作群組分為兩類:「非結構化工作群組」(Unstructured Task Group) 和「結構化工作群組」(Structured Task Group)。 PPL 以 task_group 類別表示非結構化工作群組,並以 structured_task_group 類別表示結構化工作群組。
重要事項 |
---|
PPL 也定義 Concurrency::parallel_invoke 演算法,這個演算法使用 structured_task_group 類別,以平行方式執行一組工作。 因為 parallel_invoke 演算法的語法比較簡潔,建議您盡可能使用它,而不要使用 structured_task_group 類別。 平行演算法主題將更詳細說明 parallel_invoke。 |
當您擁有數個要同時執行的獨立工作,而且必須等候所有工作完成才能繼續時,使用 parallel_invoke。 當您擁有數個要同時執行的獨立工作,但是要等候工作稍後完成時,使用 task_group。 例如,您可以將工作加入至 task_group 物件,並在另一個函式中或從另一個執行緒等候這些工作完成。
工作群組支援取消的概念。 取消可讓您對所有使用中工作表示您要取消整體作業。 取消也可避免啟動尚未啟動的工作。 如需取消的詳細資訊,請參閱 PPL 中的取消。
執行階段也提供例外處理模型,可讓您從工作擲回例外狀況,並在等候相關工作群組完成時處理該例外狀況。 如需此例外處理模型的詳細資訊,請參閱並行執行階段的例外處理。
比較 task_group 與 structured_task_group
雖然我們建議您使用 task_group 或 parallel_invoke,而不使用 structured_task_group 類別,但在某些情況下,您可能要使用 structured_task_group,例如當您撰寫可執行可變數目的工作或需要支援取消的平行演算法時。 本節說明 task_group 和 structured_task_group 類別之間的差異。
task_group 類別為安全執行緒。 因此,您可以從多個執行緒將工作加入至 task_group 物件,以及從多個執行緒等候或取消 task_group 物件。 structured_task_group 物件的建構和解構必須在相同的語彙範圍中發生。 此外,structured_task_group 物件的所有作業都必須在相同的執行緒上發生。 此規則的例外是 Concurrency::structured_task_group::cancel 和 Concurrency::structured_task_group::is_canceling 方法。 子工作隨時都可以呼叫這些方法來取消父工作群組或是檢查是否已取消。
在呼叫 Concurrency::task_group::wait 或 Concurrency::task_group::run_and_wait 方法之後,還可以在 task_group 物件上執行其他工作。 相反地,在呼叫 Concurrency::structured_task_group::wait 或 Concurrency::structured_task_group::run_and_wait 方法之後,無法在 structured_task_group 物件上執行其他工作。
因為 structured_task_group 類別不會跨執行緒進行同步處理,所以它的執行額外負荷比 task_group 類別更少。 因此,如果您的問題並不需要排定來自多個執行緒的工作,而且您無法撰寫 parallel_invoke 演算法,structured_task_group 類別有助於您撰寫執行效果更好的程式碼。
如果您在 structured_task_group 物件內部使用另一個 structured_task_group 物件,內部物件完成且終結之後,外部物件才能完成。 task_group 類別不需要巢狀工作群組先完成然後再完成外部群組。
非結構化工作群組和結構化工作群組使用工作控制代碼的方式並不相同。 您可以將工作函式直接傳遞給 task_group 物件,task_group 物件就會為您建立及管理工作控制代碼。 structured_task_group 類別需要您為每個工作管理 task_handle 物件。 每個 task_handle 物件在其相關 structured_task_group 物件的整個存留期都必須維持有效。 請使用 Concurrency::make_task 函式建立 task_handle 物件,如下列基本範例所示:
// make-task-structure.cpp
// compile with: /EHsc
#include <ppl.h>
using namespace Concurrency;
int wmain()
{
// Use the make_task function to define several tasks.
auto task1 = make_task([] { /*TODO: Define the task body.*/ });
auto task2 = make_task([] { /*TODO: Define the task body.*/ });
auto task3 = make_task([] { /*TODO: Define the task body.*/ });
// Create a structured task group and run the tasks concurrently.
structured_task_group tasks;
tasks.run(task1);
tasks.run(task2);
tasks.run_and_wait(task3);
}
若要在擁有可變數目的工作時管理工作控制代碼,請使用堆疊配置常式 (例如 _malloca) 或容器類別 (例如 std::vector)。
task_group 和 structured_task_group 都支援取消。 如需取消的詳細資訊,請參閱 PPL 中的取消。
範例
下列基本範例顯示如何使用工作群組。 這個範例會使用 parallel_invoke 演算法,同時執行兩個工作。 每個工作都會將子任務加入至 task_group 物件。 請注意,task_group 類別允許同時加入多個工作。
// using-task-groups.cpp
// compile with: /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>
using namespace Concurrency;
using namespace std;
// Prints a message to the console.
template<typename T>
void print_message(T t)
{
wstringstream ss;
ss << L"Message from task: " << t << endl;
wcout << ss.str();
}
int wmain()
{
// A task_group object that can be used from multiple threads.
task_group tasks;
// Concurrently add several tasks to the task_group object.
parallel_invoke(
[&] {
// Add a few tasks to the task_group object.
tasks.run([] { print_message(L"Hello"); });
tasks.run([] { print_message(42); });
},
[&] {
// Add one additional task to the task_group object.
tasks.run([] { print_message(3.14); });
}
);
// Wait for all tasks to finish.
tasks.wait();
}
下列是這個範例 (Example) 的範例 (Sample) 輸出:
Message from task: Hello
Message from task: 3.14
Message from task: 42
因為 parallel_invoke 演算法會並行執行工作,所以輸出訊息的順序可能會不同。
如需示範如何使用 parallel_invoke 演算法的完整範例,請參閱 HOW TO:使用 parallel_invoke 來撰寫平行排序常式和 HOW TO:使用 parallel_invoke 執行平行作業。 如需使用 task_group 類別來實作非同步未來的完整範例,請參閱逐步解說:實作未來。
健全的程式設計
在使用工作群組和平行演算法時,請確定您了解取消和例外處理的角色。 例如,在平行工作的樹狀結構中,已取消的工作會導致子工作無法執行。 如果其中一個子工作所執行的作業對應用程式很重要,例如釋放資源,這可能會造成問題。 此外,如果子工作擲回例外狀況,該例外狀況可能會透過物件解構函式傳播,而在應用程式中造成未定義的行為。 如需說明這些重點的範例,請參閱<平行模式程式庫中的最佳作法>文件的了解取消和例外處理對物件解構的影響一節。 如需 PPL 中取消和例外處理模型的詳細資訊,請參閱 PPL 中的取消和並行執行階段的例外處理。
相關主題
HOW TO:使用 parallel_invoke 來撰寫平行排序常式
顯示如何使用 parallel_invoke 演算法改善雙調排序演算法的效能。HOW TO:使用 parallel_invoke 執行平行作業
顯示如何使用 parallel_invoke 演算法,改善對共用資料來源執行多個作業的程式效能。逐步解說:實作未來
顯示如何將並行執行階段中的現有功能合併成可完成更多工作的單一項目。平行模式程式庫 (PPL)
說明 PPL,PPL 提供開發並行應用程式時的重要程式設計模型。
參考
變更記錄
日期 |
記錄 |
原因 |
---|---|---|
2011 年 3 月 |
加入在使用工作群組和平行演算法時取消和例外處理角色的資訊。 |
資訊加強。 |
2010 年 7 月 |
重新組織內容。 |
資訊加強。 |
2010 年 5 月 |
擴充方針。 |
資訊加強。 |