다음을 통해 공유


방법: 중첩된 작업 풀기

다음 예제와 같이 메서드에서 작업을 반환한 다음 해당 작업을 기다리거나 계속할 수 있습니다.

static Task<string> DoWorkAsync() => 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.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main()
        {
            // An arbitrary threshold value.
            byte threshold = 0x40;

            // data is a Task<byte[]>
            Task<byte[]> data = Task<byte[]>.Factory.StartNew(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[]>
            Task<byte> 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.
            Task<Task> lastStep = stepTwo.ContinueWith((antecedent) =>
                {
                    if (antecedent.Result >= threshold)
                    {
                        return Task.Factory.StartNew(() =>
                            Console.WriteLine($"Program complete. Final = 0x{stepTwo.Result:x} threshold = 0x{threshold:x}"));
                    }
                    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();
            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($"{final:x}");
            }
            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

참고하십시오