답변:
두 답변 모두 기다릴 수있는 언급하지 않았습니다 Task.WhenAll
.
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
주요 차이점 Task.WaitAll
과 Task.WhenAll
전 의지 블록 (하여 유사한이다 Wait
후자 동안 단일 작업에서)이 모든 작업이 완료 될 때까지 호출자에게 제어를 다시 산출 기다리게 될 수 없다.
따라서 예외 처리는 다릅니다.
Task.WaitAll
:
하나 이상의 Task 인스턴스가 취소되었거나 하나 이상의 Task 인스턴스를 실행하는 동안 예외가 발생했습니다. 작업이 취소 된 경우 AggregateException에는 InnerExceptions 컬렉션에 OperationCanceledException이 포함됩니다.
Task.WhenAll
:
제공된 작업 중 하나가 오류 상태에서 완료되면 반환 된 작업도 오류 상태에서 완료됩니다. 여기에는 예외에 제공된 각 작업의 래핑되지 않은 예외 집합이 포함됩니다.
제공된 작업 중 하나라도 오류가 발생했지만 그 중 하나 이상이 취소 된 경우 반환 된 작업은 취소됨 상태로 종료됩니다.
오류가 발생한 작업이없고 취소 된 작업이 없으면 결과 작업은 RanToCompletion 상태로 종료됩니다. 제공된 배열 / 열거 가능한 작업이없는 경우 반환 된 작업은 호출자에게 반환되기 전에 즉시 RanToCompletion 상태로 전환됩니다.
Task.WhenAll
는 당신을 위해 작업을 시작하지 않습니다. "핫"을 제공해야합니다. 이미 시작했음을 의미합니다.
StartNew
비동기 적으로 대기하는 모든 작업과 회전하는 새로운 작업은 무엇 과 관련이 있습니까?
다음과 같은 많은 작업을 만들 수 있습니다.
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
내가 본 가장 좋은 옵션은 다음 확장 방법입니다.
public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
return Task.WhenAll(sequence.Select(action));
}
다음과 같이 호출하십시오.
await sequence.ForEachAsync(item => item.SomethingAsync(blah));
또는 비동기 람다로 :
await sequence.ForEachAsync(async item => {
var more = await GetMoreAsync(item);
await more.FrobbleAsync();
});
당신은 사용할 수 있습니다 WhenAll
awaitable 반환하는 Task
또는 WaitAll
더 리턴 유형이없는과에 더 코드 실행 시뮬을 차단합니다 Thread.Sleep
모든 작업이 완료 취소 또는 폴트 때까지.
예
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
당신이 praticular 순서로 작업을 실행하려는 경우 양식 영감을 얻을 수있는 이 anwser를.
await
각 작업과 동시에 사용에 WaitAll
나 WhenAll
. Task[]
초기화 작업이 없어야 await
합니까?
Task
s 를 체인으로 연결 하시겠습니까 , 아니면 병렬로 호출 할 수 있습니까?
체인의 경우
그냥 그런 짓을
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
결함이있을 수 있으므로 Task
각각 의 이전 인스턴스 를 확인하는 것을 잊지 마십시오 ContinueWith
.
병렬 방식으로
가장 간단한 방법은 다음과 같습니다. Parallel.Invoke
그렇지 않으면 s를 Task.WaitAll
사용 WaitHandle
하여 0 동작 남은 횟수에 카운트 다운을 수행 할 수도 있습니다 (대기, 새 클래스가 있습니다 CountdownEvent
) 또는 ...
이것이 Func <> 배열로 수행하는 방법입니다 .
var tasks = new Func<Task>[]
{
() => myAsyncWork1(),
() => myAsyncWork2(),
() => myAsyncWork3()
};
await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync
또 다른 대답 ...하지만 일반적으로 데이터를 동시에로드하고 변수와 같은 변수에 넣어야하는 경우가 있습니다.
var cats = new List<Cat>();
var dog = new Dog();
var loadDataTasks = new Task[]
{
Task.Run(async () => cats = await LoadCatsAsync()),
Task.Run(async () => dog = await LoadDogAsync())
};
try
{
await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
// handle exception
}
LoadCatsAsync()
와 LoadDogAsync()
단지 데이터베이스 호출입니다 그들은 IO 바인딩입니다. Task.Run()
CPU 바운드 작업을위한 것입니다. 데이터베이스 서버의 응답을 기다리는 것만으로 불필요한 오버 헤드가 추가됩니다. Yuval의 승인 된 답변은 IO 바운드 작업에 적합한 방법입니다.
이 시나리오 중 일부에서 작업을 사용하는 방법을 보여주는 코드를 준비했습니다.
// method to run tasks in a parallel
public async Task RunMultipleTaskParallel(Task[] tasks) {
await Task.WhenAll(tasks);
}
// methode to run task one by one
public async Task RunMultipleTaskOneByOne(Task[] tasks)
{
for (int i = 0; i < tasks.Length - 1; i++)
await tasks[i];
}
// method to run i task in parallel
public async Task RunMultipleTaskParallel(Task[] tasks, int i)
{
var countTask = tasks.Length;
var remainTasks = 0;
do
{
int toTake = (countTask < i) ? countTask : i;
var limitedTasks = tasks.Skip(remainTasks)
.Take(toTake);
remainTasks += toTake;
await RunMultipleTaskParallel(limitedTasks.ToArray());
} while (remainTasks < countTask);
}
await Task.WhenAll(task1, task2);
합니까?