Task.WhenAll
.NET Core 3.0에서 실행할 때 방법 에 대해 궁금한 점을 확인했습니다 . 간단한 Task.Delay
작업을에 단일 인수로 전달 Task.WhenAll
했으며 래핑 된 작업이 원래 작업과 동일하게 작동 할 것으로 예상했습니다. 그러나 이것은 사실이 아닙니다. 원래 작업의 연속은 비동기식으로 (바람직하게) Task.WhenAll(task)
실행되고 여러 래퍼 의 연속은 연속적으로 동 기적으로 실행됩니다 (바람직하지 않음).
다음은 이 동작 의 데모 입니다. 4 명의 작업자 작업이 동일한 Task.Delay
작업을 완료하기를 기다리고있다 가 무거운 계산을 계속합니다 (로 시뮬레이션 됨 Thread.Sleep
).
var task = Task.Delay(500);
var workers = Enumerable.Range(1, 4).Select(async x =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
$" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} before await");
await task;
//await Task.WhenAll(task);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
$" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} after await");
Thread.Sleep(1000); // Simulate some heavy CPU-bound computation
}).ToArray();
Task.WaitAll(workers);
출력은 다음과 같습니다. 4 개의 연속이 다른 스레드에서 예상대로 실행됩니다 (병렬).
05:23:25.511 [1] Worker1 before await
05:23:25.542 [1] Worker2 before await
05:23:25.543 [1] Worker3 before await
05:23:25.543 [1] Worker4 before await
05:23:25.610 [4] Worker1 after await
05:23:25.610 [7] Worker2 after await
05:23:25.610 [6] Worker3 after await
05:23:25.610 [5] Worker4 after await
이제 줄 await task
을 주석 처리하고 다음 줄의 주석 처리를 제거 await Task.WhenAll(task)
하면 출력이 상당히 다릅니다. 모든 연속체가 동일한 스레드에서 실행되므로 계산이 병렬화되지 않습니다. 각 계산은 이전 계산이 완료된 후 시작됩니다.
05:23:46.550 [1] Worker1 before await
05:23:46.575 [1] Worker2 before await
05:23:46.576 [1] Worker3 before await
05:23:46.576 [1] Worker4 before await
05:23:46.645 [4] Worker1 after await
05:23:47.648 [4] Worker2 after await
05:23:48.650 [4] Worker3 after await
05:23:49.651 [4] Worker4 after await
놀랍게도 이것은 각 작업자가 다른 랩퍼를 기다리는 경우에만 발생합니다. 래퍼를 미리 정의하면 :
var task = Task.WhenAll(Task.Delay(500));
... 그리고 await
모든 작업자 내에서 동일한 작업을 수행하면 동작은 첫 번째 경우와 동일합니다 (비동기 연속).
내 질문은 : 왜 이런 일이 발생합니까? 동일한 작업의 다른 래퍼의 연속이 동일한 스레드에서 동기식으로 실행되는 원인은 무엇입니까?
참고 :Task.WhenAny
대신 작업을 래핑 Task.WhenAll
하면 동일한 이상한 동작이 발생합니다.
또 다른 관찰 : 래퍼를 a 안에 래핑 Task.Run
하면 연속성이 비 동기화 될 것으로 예상했습니다 . 그러나 일어나지 않습니다. 아래 줄의 연속은 여전히 동일한 스레드에서 (동기식으로) 실행됩니다.
await Task.Run(async () => await Task.WhenAll(task));
설명 : 위의 차이점은 .NET Core 3.0 플랫폼에서 실행되는 콘솔 응용 프로그램에서 관찰되었습니다. .NET Framework 4.8에서는 원래 작업을 기다리는 것과 작업 래퍼를 기다리는 것 사이에는 차이가 없습니다. 두 경우 모두 연속이 동일한 스레드에서 동기식으로 실행됩니다.
Task.WhenAll
Task.Delay
에서 100
로 변경 한 후에 만 가능 합니다. 1000
await
await Task.WhenAll(new[] { task });
어떻게됩니까?