方法:入れ子のタスクのラップを解除する
以下の例に示すように、メソッドからタスクを返して、そのタスクで待機またはそのタスクから続行することができます。
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
関連項目
.NET