巢狀工作和子工作
巢狀工作是指在另一個工作的使用者委派中建立的 Task 執行個體。 「子工作」(Child Task) 是以 AttachedToParent 選項建立的巢狀工作。 只要系統資源足夠,一個工作可以建立任何數目的子工作和 (或) 巢狀工作。 下列範例顯示一個父工作如何建立一個簡單的巢狀工作。
Shared Sub SimpleNestedTask()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Outer task executing.")
Dim child = Task.Factory.StartNew(Sub()
Console.WriteLine("Nested task starting.")
Thread.SpinWait(500000)
Console.WriteLine("Nested task completing.")
End Sub)
End Sub)
parent.Wait()
Console.WriteLine("Outer task has completed.")
End Sub
' Sample output:
' Outer task executing.
' Nested task starting.
' Outer task has completed.
' Nested task completing.
static void SimpleNestedTask()
{
var parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Outer task executing.");
var child = Task.Factory.StartNew(() =>
{
Console.WriteLine("Nested task starting.");
Thread.SpinWait(500000);
Console.WriteLine("Nested task completing.");
});
});
parent.Wait();
Console.WriteLine("Outer has completed.");
}
/* Sample output:
Outer task executing.
Nested task starting.
Outer has completed.
Nested task completing.
*/
附加的子工作和中斷連結之巢狀工作的比較
子工作和巢狀工作最重要的一個分野,就是 巢狀工作其實與父工作或外部工作沒有任何關係,而附加的子工作則會與父代緊密同步。 如果您將工作建立陳述式變更為使用 AttachedToParent 選項,如下列範例所示,
Dim child = Task.Factory.StartNew(Sub()
Console.WriteLine("Attached child starting.")
Thread.SpinWait(5000000)
Console.WriteLine("Attached child completing.")
End Sub, TaskCreationOptions.AttachedToParent)
var child = Task.Factory.StartNew((t) =>
{
Console.WriteLine("Attached child starting.");
Thread.SpinWait(5000000);
Console.WriteLine("Attached child completing.");
}, TaskCreationOptions.AttachedToParent);
則會產生下列輸出。
' Parent task executing.
' Attached child starting.
' Attached child completing.
' Parent has completed.
Parent task executing.
Attached child starting.
Attached child completing.
Parent has completed.
您可以使用附加的子工作來建立非同步作業的緊密同步處理圖。 不過在大部分情況下,建議您還是使用巢狀工作,因為巢狀工作與其他工作的關聯性較不複雜。 這就是為什麼在其他工作內建立的工作預設會是巢狀工作,而您必須明確指定 AttachedToParent 選項才能建立子工作的原因。
下表列出這兩種子工作之間的基本差異。
分類 |
巢狀工作 |
附加的子工作 |
---|---|---|
外部工作 (父代) 會等候內部工作完成。 |
否 |
是 |
父代會傳播由子系 (內部工作) 所擲回的例外狀況。 |
否 |
是 |
父代 (外部工作) 的狀態取決於子系 (內部工作) 的狀態。 |
否 |
是 |
在中斷連結的巢狀工作為 Task<TResult> 的情節中,您仍然可以透過存取巢狀工作的 Result 屬性,來強制父代等候子系。 Result 屬性會封鎖到其工作完成為止。
Shared Sub WaitForSimpleNestedTask()
Dim parent = Task(Of Integer).Factory.StartNew(Function()
Console.WriteLine("Outer task executing.")
Dim child = Task(Of Integer).Factory.StartNew(Function()
Console.WriteLine("Nested task starting.")
Thread.SpinWait(5000000)
Console.WriteLine("Nested task completing.")
Return 42
End Function)
Return child.Result
End Function)
Console.WriteLine("Outer has returned {0}", parent.Result)
End Sub
'Sample output:
' Outer task executing.
' Nested task starting.
' Detached task completing.
' Outer has returned 42
static void WaitForSimpleNestedTask()
{
var outer = Task<int>.Factory.StartNew(() =>
{
Console.WriteLine("Outer task executing.");
var nested = Task<int>.Factory.StartNew(() =>
{
Console.WriteLine("Nested task starting.");
Thread.SpinWait(5000000);
Console.WriteLine("Nested task completing.");
return 42;
});
// Parent will wait for this detached child.
return nested.Result;
});
Console.WriteLine("Outer has returned {0}.", outer.Result);
}
/* Sample output:
Outer task executing.
Nested task starting.
Nested task completing.
Outer has returned 42.
*/
巢狀工作和子工作中的例外狀況
就像任何非巢狀工作一樣,如果巢狀工作擲回例外狀況,則必須在外部工作中觀察或直接處理該例外狀況。 如果附加的子系擲回例外狀況,則該例外狀況會自動傳播至父工作,然後再傳播回正在等候或嘗試存取工作之 Result 屬性的執行緒。 因此,使用附加的子工作,您只要在一個點 (也就是呼叫端執行緒上的 Wait 呼叫) 就可以處理所有例外狀況。 如需詳細資訊,請參閱例外處理 (工作平行程式庫)。
取消和子工作
之前提過工作取消是一項合作式作業。 因此,每個附加的或中斷連結的子工作都必須監視取消語彙基元的狀態,才能變成「可取消」。 如果您要使用一個取消要求來取消父代和其所有子系,請將相同的語彙基元當做引數傳遞給所有工作,並在每個工作中提供邏輯來回應要求。 如需詳細資訊,請參閱工作取消和 HOW TO:取消工作及其子系。
當父代取消時
如果父代在子系啟動之前就將自己取消,則子系 (巢狀) 工作當然永遠都不會啟動。 如果父代在子系或巢狀工作啟動之後才將自己取消,則巢狀 (子系) 工作會執行到完成為止,除非它還有自己的取消邏輯。 如需詳細資訊,請參閱 工作取消。
當巢狀工作取消時
如果中斷連結的子工作使用當初傳遞給工作的相同語彙基元將自己取消,而且父代沒有在等候子系完成,則不會傳播任何例外狀況,因為系統會將例外狀況視為良性的合作取消結果。 這個行為與任何最上層工作的行為都相同。
當子工作取消時
當附加的子工作使用當初傳遞給工作的相同語彙基元將自己取消時,則 TaskCanceledException 會放在 AggregateException 內傳播給聯結的執行緒。 請務必要等候父工作完成,這樣您才能夠處理所有沿著附加子工作圖往上傳播的良性和惡性例外狀況。
如需詳細資訊,請參閱例外處理 (工作平行程式庫)。