如何:解除嵌套任务的包装
可以从方法返回任务,再等待或继续执行此任务,如下面的示例所示:
static Task<string> DoWorkAsync()
{
return Task<String>.Factory.StartNew(() =>
{
//...
return "Work completed.";
});
}
static void StartTask()
{
Task<String> t = DoWorkAsync();
t.Wait();
Console.WriteLine(t.Result);
}
Shared Function DoWorkAsync() As Task(Of String)
Return Task(Of String).Run(Function()
'...
Return "Work completed."
End Function)
End Function
Shared Sub StartTask()
Dim t As Task(Of String) = DoWorkAsync()
t.Wait()
Console.WriteLine(t.Result)
End Sub
在上面的示例中,Result 属性的类型为 string
(Visual Basic 中的 String
)。
不过,在某些情况下,建议在另一个任务中创建任务,再返回嵌套任务。 在这种情况下,封闭任务的 TResult
本身就是任务。 在下面的示例中,Result 属性是 C# 中的 Task<Task<string>>
或 Visual Basic 中的 Task(Of Task(Of String))
。
// Note the type of t and t2.
Task<Task<string>> t = Task.Factory.StartNew(() => DoWorkAsync());
Task<Task<string>> t2 = DoWorkAsync().ContinueWith((s) => DoMoreWorkAsync());
// Outputs: System.Threading.Tasks.Task`1[System.String]
Console.WriteLine(t.Result);
' Note the type of t and t2.
Dim t As Task(Of Task(Of String)) = Task.Run(Function() DoWorkAsync())
Dim t2 As Task(Of Task(Of String)) = DoWorkAsync().ContinueWith(Function(s) DoMoreWorkAsync())
' Outputs: System.Threading.Tasks.Task`1[System.String]
Console.WriteLine(t.Result)
虽然可以编写代码来取消包装外部任务并检索原始任务及其 Result 属性,但此类代码不易编写,因为必须处理异常和取消请求。 在这种情况下,建议使用 Unwrap 扩展方法之一,如下面的示例所示。
// Unwrap the inner task.
Task<string> t3 = DoWorkAsync().ContinueWith((s) => DoMoreWorkAsync()).Unwrap();
// Outputs "More work completed."
Console.WriteLine(t.Result);
' Unwrap the inner task.
Dim t3 As Task(Of String) = DoWorkAsync().ContinueWith(Function(s) DoMoreWorkAsync()).Unwrap()
' Outputs "More work completed."
Console.WriteLine(t.Result)
Unwrap 方法可用于将任何 Task<Task>
或 Task<Task<TResult>>
(Visual Basic 中的 Task(Of Task)
或 Task(Of Task(Of TResult))
)转换为 Task
或 Task<TResult>
(Visual Basic 中的 Task(Of TResult)
)。 新任务完全表示内部嵌套任务,并包含取消状态和所有异常。
示例
下面的示例展示了如何使用 Unwrap 扩展方法。
namespace Unwrap
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
// A program whose only use is to demonstrate Unwrap.
class Program
{
static void Main()
{
// An arbitrary threshold value.
byte threshold = 0x40;
// data is a Task<byte[]>
var data = Task<byte[]>.Factory.StartNew(() =>
{
return GetData();
});
// We want to return a task so that we can
// continue from it later in the program.
// Without Unwrap: stepTwo is a Task<Task<byte[]>>
// With Unwrap: stepTwo is a Task<byte[]>
var stepTwo = data.ContinueWith((antecedent) =>
{
return Task<byte>.Factory.StartNew( () => Compute(antecedent.Result));
})
.Unwrap();
// Without Unwrap: antecedent.Result = Task<byte>
// and the following method will not compile.
// With Unwrap: antecedent.Result = byte and
// we can work directly with the result of the Compute method.
var lastStep = stepTwo.ContinueWith( (antecedent) =>
{
if (antecedent.Result >= threshold)
{
return Task.Factory.StartNew( () => Console.WriteLine("Program complete. Final = 0x{0:x} threshold = 0x{1:x}", stepTwo.Result, threshold));
}
else
{
return DoSomeOtherAsynchronousWork(stepTwo.Result, threshold);
}
});
lastStep.Wait();
Console.WriteLine("Press any key");
Console.ReadKey();
}
#region Dummy_Methods
private static byte[] GetData()
{
Random rand = new Random();
byte[] bytes = new byte[64];
rand.NextBytes(bytes);
return bytes;
}
static Task DoSomeOtherAsynchronousWork(int i, byte b2)
{
return Task.Factory.StartNew(() =>
{
Thread.SpinWait(500000);
Console.WriteLine("Doing more work. Value was <= threshold");
});
}
static byte Compute(byte[] data)
{
byte final = 0;
foreach (byte item in data)
{
final ^= item;
Console.WriteLine("{0:x}", final);
}
Console.WriteLine("Done computing");
return final;
}
#endregion
}
}
'How to: Unwrap a Task
Imports System.Threading
Imports System.Threading.Tasks
Module UnwrapATask2
Sub Main()
' An arbitrary threshold value.
Dim threshold As Byte = &H40
' myData is a Task(Of Byte())
Dim myData As Task(Of Byte()) = Task.Factory.StartNew(Function()
Return GetData()
End Function)
' We want to return a task so that we can
' continue from it later in the program.
' Without Unwrap: stepTwo is a Task(Of Task(Of Byte))
' With Unwrap: stepTwo is a Task(Of Byte)
Dim stepTwo = myData.ContinueWith(Function(antecedent)
Return Task.Factory.StartNew(Function()
Return Compute(antecedent.Result)
End Function)
End Function).Unwrap()
Dim lastStep = stepTwo.ContinueWith(Function(antecedent)
Console.WriteLine("Result = {0}", antecedent.Result)
If antecedent.Result >= threshold Then
Return Task.Factory.StartNew(Sub()
Console.WriteLine("Program complete. Final = &H{1:x} threshold = &H{1:x}",
stepTwo.Result, threshold)
End Sub)
Else
Return DoSomeOtherAsynchronousWork(stepTwo.Result, threshold)
End If
End Function)
Try
lastStep.Wait()
Catch ae As AggregateException
For Each ex As Exception In ae.InnerExceptions
Console.WriteLine(ex.Message & ex.StackTrace & ex.GetBaseException.ToString())
Next
End Try
Console.WriteLine("Press any key")
Console.ReadKey()
End Sub
#Region "Dummy_Methods"
Function GetData() As Byte()
Dim rand As Random = New Random()
Dim bytes(64) As Byte
rand.NextBytes(bytes)
Return bytes
End Function
Function DoSomeOtherAsynchronousWork(ByVal i As Integer, ByVal b2 As Byte) As Task
Return Task.Factory.StartNew(Sub()
Thread.SpinWait(500000)
Console.WriteLine("Doing more work. Value was <= threshold.")
End Sub)
End Function
Function Compute(ByVal d As Byte()) As Byte
Dim final As Byte = 0
For Each item As Byte In d
final = final Xor item
Console.WriteLine("{0:x}", final)
Next
Console.WriteLine("Done computing")
Return final
End Function
#End Region
End Module