내용 보기

작성자

관리자 (IP : 172.17.0.1)

날짜

2020-07-13 04:50

제목

[C#] Task.ContinueWith 설명


Task 타입에 제공되는 ContinueWith 메서드는 Task의 본래 작업이 완료된 후 실행될 작업을 등록하는 기능을 제공합니다. 가령 다음과 같이 코딩을 하면,

Task<int> task = new Task<int>(() =>
{
Console.WriteLine("TEST0");
Thread.Sleep(5000);
return 1;
});

task.Start();

task.ContinueWith((arg) =>
{
Console.WriteLine("TEST1");
});


화면에는 "TEST0"에 이어 5초 후 "TEST1"이 출력됩니다. 그런데, 이 코드를 보면서 몇 가지 궁금함이 생깁니다.

  1. 원래의 작업을 마친 Task의 스레드는 ContinueWith로 등록된 메서드를 이어서 실행해 주는 걸까?
  2. 만약 이어서 실행하는 거라면 Task의 작업이 이미 종료된 경우라면 어떨까?


우선 위의 질문에 대해 2번부터 확인해 보겠습니다. 방법은, 간단하게 다음과 같이 코딩해 보면 됩니다. ^^

Task<int> task = new Task<int>(() =>
{
Console.WriteLine("TEST0");
Thread.Sleep(5000);
return 1;
});

task.Start();
Thread.Sleep(6000); // task 작업이 완료될 수 있도록.

task.ContinueWith((arg) =>
{
Console.WriteLine("TEST1");
});


확인 결과, Task의 원래 작업이 종료된 이후에도 ContinueWith는 잘 실행됩니다. 그럼, 또 궁금해집니다. 이미 Task의 스레드는 Thread Pool에 반환된 상태인데, 저런 경우라면 ContinueWith를 호출한 스레드가 그냥 이어서 실행하는 것인지, 아니면 그것도 결국 Thread Pool에 맡기는 것인지?

역시 이것도 테스트해 보면 알 수 있습니다.

...
{
Task<int> task = new Task<int>(() =>
{
WriteLog("TEST0");
Thread.Sleep(5000);
return 1;
});

task.Start();

task.ContinueWith((arg) =>
{
WriteLog("TEST1");
});

Thread.Sleep(6000); // task 작업이 완료될 수 있도록.

task.ContinueWith((arg) =>
{
WriteLog("TEST2");
});
}

private static void WriteLog(string text)
{
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] " + text);
}


확인 결과, ContinueWith는 원본 Task의 작업 종료 여부에 상관없이 Thread Pool로부터 할당받은 스레드에 의해서 실행됩니다.




ContinueWith로 지정한 작업을 다음과 같은 조건으로 실행할 수도 있습니다.

  • Task의 작업이 종료되지 않은 경우, 그 스레드에서 이어서 실행
  • Task의 작업이 종료된 경우, ContinueWith를 호출한 스레드에서 실행


방법은, 다음과 같이 TaskContinuationOptions.ExecuteSynchronously 옵션을 주면 됩니다.

{
Task<int> task = new Task<int>(() =>
{
WriteLog("TEST0");
Thread.Sleep(5000);
return 1;
});

task.Start();

task.ContinueWith((arg) =>
{
WriteLog("TEST1"); // task에 할당된 스레드에 의해 실행
}, TaskContinuationOptions.ExecuteSynchronously);

Console.WriteLine(task.IsCompleted);
Thread.Sleep(6000);
Console.WriteLine(task.IsCompleted);

task.ContinueWith((arg) =>
{
WriteLog("TEST2"); // ContinueWith를 호출한 현재 스레드에 의해 실행
}, TaskContinuationOptions.ExecuteSynchronously);

Console.ReadLine();
}


참고로 TaskContinuationOptions으로 제공할 수 있는 옵션은 다음과 같습니다.

public enum TaskContinuationOptions
{
None = 0,
PreferFairness = 1,
LongRunning = 2,
AttachedToParent = 4,
DenyChildAttach = 8,
HideScheduler = 16,
LazyCancellation = 32,
RunContinuationsAsynchronously = 64,
NotOnRanToCompletion = 65536,
NotOnFaulted = 131072,
OnlyOnCanceled = 196608,
NotOnCanceled = 262144,
OnlyOnFaulted = 327680,
OnlyOnRanToCompletion = 393216,
ExecuteSynchronously = 524288
}

출처1

http://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0&detail=1&wid=11457

출처2