如何:防止子工作附加到其父代
本文件將示範如何防止將子工作附加至父工作。 如果您呼叫的元件是由協力廠商所撰寫,而且也會使用工作,則防止子工作附加至其父代會很實用。 例如,如果使用 TaskCreationOptions.AttachedToParent 選項建立 Task 或 Task<TResult> 物件的協力廠商元件會長時間執行或擲回未處理的例外狀況,則可能造成程式碼發生問題。
範例
下列範例會比較使用預設選項的效果,以及防止子工作附加至父工作的效果。 這個範例會建立 Task 物件,該物件會呼叫同樣使用 Task 物件的協力廠商程式庫。 協力廠商程式庫會使用 AttachedToParent 選項建立 Task 物件。 應用程式會使用 TaskCreationOptions.DenyChildAttach 選項建立父工作。 這個選項會指示執行階段移除子工作中的 AttachedToParent 規格。
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
// Defines functionality that is provided by a third-party.
// In a real-world scenario, this would likely be provided
// in a separate code file or assembly.
namespace Contoso
{
public class Widget
{
public Task Run()
{
// Create a long-running task that is attached to the
// parent in the task hierarchy.
return Task.Factory.StartNew(() =>
{
// Simulate a lengthy operation.
Thread.Sleep(5000);
}, TaskCreationOptions.AttachedToParent);
}
}
}
// Demonstrates how to prevent a child task from attaching to the parent.
class DenyChildAttach
{
static void RunWidget(Contoso.Widget widget,
TaskCreationOptions parentTaskOptions)
{
// Record the time required to run the parent
// and child tasks.
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("Starting widget as a background task...");
// Run the widget task in the background.
Task<Task> runWidget = Task.Factory.StartNew(() =>
{
Task widgetTask = widget.Run();
// Perform other work while the task runs...
Thread.Sleep(1000);
return widgetTask;
}, parentTaskOptions);
// Wait for the parent task to finish.
Console.WriteLine("Waiting for parent task to finish...");
runWidget.Wait();
Console.WriteLine("Parent task has finished. Elapsed time is {0} ms.",
stopwatch.ElapsedMilliseconds);
// Perform more work...
Console.WriteLine("Performing more work on the main thread...");
Thread.Sleep(2000);
Console.WriteLine("Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds);
// Wait for the child task to finish.
Console.WriteLine("Waiting for child task to finish...");
runWidget.Result.Wait();
Console.WriteLine("Child task has finished. Elapsed time is {0} ms.",
stopwatch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
Contoso.Widget w = new Contoso.Widget();
// Perform the same operation two times. The first time, the operation
// is performed by using the default task creation options. The second
// time, the operation is performed by using the DenyChildAttach option
// in the parent task.
Console.WriteLine("Demonstrating parent/child tasks with default options...");
RunWidget(w, TaskCreationOptions.None);
Console.WriteLine();
Console.WriteLine("Demonstrating parent/child tasks with the DenyChildAttach option...");
RunWidget(w, TaskCreationOptions.DenyChildAttach);
}
}
/* Sample output:
Demonstrating parent/child tasks with default options...
Starting widget as a background task...
Waiting for parent task to finish...
Parent task has finished. Elapsed time is 5014 ms.
Performing more work on the main thread...
Elapsed time is 7019 ms.
Waiting for child task to finish...
Child task has finished. Elapsed time is 7019 ms.
Demonstrating parent/child tasks with the DenyChildAttach option...
Starting widget as a background task...
Waiting for parent task to finish...
Parent task has finished. Elapsed time is 1007 ms.
Performing more work on the main thread...
Elapsed time is 3015 ms.
Waiting for child task to finish...
Child task has finished. Elapsed time is 5015 ms.
*/
Imports System.Diagnostics
Imports System.Threading
Imports System.Threading.Tasks
' Defines functionality that is provided by a third-party.
' In a real-world scenario, this would likely be provided
' in a separate code file or assembly.
Namespace Contoso
Public Class Widget
Public Function Run() As Task
' Create a long-running task that is attached to the
' parent in the task hierarchy.
Return Task.Factory.StartNew(Sub() Thread.Sleep(5000), TaskCreationOptions.AttachedToParent)
' Simulate a lengthy operation.
End Function
End Class
End Namespace
' Demonstrates how to prevent a child task from attaching to the parent.
Friend Class DenyChildAttach
Private Shared Sub RunWidget(ByVal widget As Contoso.Widget, ByVal parentTaskOptions As TaskCreationOptions)
' Record the time required to run the parent
' and child tasks.
Dim stopwatch As New Stopwatch()
stopwatch.Start()
Console.WriteLine("Starting widget as a background task...")
' Run the widget task in the background.
Dim runWidget As Task(Of Task) = Task.Factory.StartNew(Function()
' Perform other work while the task runs...
Dim widgetTask As Task = widget.Run()
Thread.Sleep(1000)
Return widgetTask
End Function, parentTaskOptions)
' Wait for the parent task to finish.
Console.WriteLine("Waiting for parent task to finish...")
runWidget.Wait()
Console.WriteLine("Parent task has finished. Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)
' Perform more work...
Console.WriteLine("Performing more work on the main thread...")
Thread.Sleep(2000)
Console.WriteLine("Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)
' Wait for the child task to finish.
Console.WriteLine("Waiting for child task to finish...")
runWidget.Result.Wait()
Console.WriteLine("Child task has finished. Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)
End Sub
Shared Sub Main(ByVal args() As String)
Dim w As New Contoso.Widget()
' Perform the same operation two times. The first time, the operation
' is performed by using the default task creation options. The second
' time, the operation is performed by using the DenyChildAttach option
' in the parent task.
Console.WriteLine("Demonstrating parent/child tasks with default options...")
RunWidget(w, TaskCreationOptions.None)
Console.WriteLine()
Console.WriteLine("Demonstrating parent/child tasks with the DenyChildAttach option...")
RunWidget(w, TaskCreationOptions.DenyChildAttach)
End Sub
End Class
' Sample output:
'Demonstrating parent/child tasks with default options...
'Starting widget as a background task...
'Waiting for parent task to finish...
'Parent task has finished. Elapsed time is 5014 ms.
'Performing more work on the main thread...
'Elapsed time is 7019 ms.
'Waiting for child task to finish...
'Child task has finished. Elapsed time is 7019 ms.
'
'Demonstrating parent/child tasks with the DenyChildAttach option...
'Starting widget as a background task...
'Waiting for parent task to finish...
'Parent task has finished. Elapsed time is 1007 ms.
'Performing more work on the main thread...
'Elapsed time is 3015 ms.
'Waiting for child task to finish...
'Child task has finished. Elapsed time is 5015 ms.
'
由於父工作會在所有子工作完成後才完成,因此長時間執行的子工作可能造成整個應用程式效能不佳。 在這個範例中,當應用程式使用預設選項建立父工作時,子工作必須在父工作完成之前完成。 當應用程式使用 TaskCreationOptions.DenyChildAttach 選項時,子工作不會附加至父工作。 因此,應用程式可以在父工作完成之後,以及必須等候子工作完成之前執行其他工作。