[C#] 여러 개의 비동기 작업을 실행하고 모두 완료되기를 기다리는 중

콘솔 응용 프로그램에서 여러 비동기 작업을 실행하고 추가 처리 전에 모두 완료 될 때까지 기다려야합니다.

많은 기사가 있지만 읽을수록 많이 혼란스러워하는 것 같습니다. 작업 라이브러리의 기본 원칙을 읽고 이해했지만 어딘가에 링크가 명확하게 없습니다.

나는 다른 완료 (시작한 모든 기사에 대한 시나리오 임) 후에 시작되도록 작업을 연결할 수 있지만 모든 작업이 동시에 실행되기를 원하며 한 번 알고 싶습니다. 그들은 모두 완료되었습니다.

이와 같은 시나리오의 가장 간단한 구현은 무엇입니까?



답변

두 답변 모두 기다릴 수있는 언급하지 않았습니다 Task.WhenAll.

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

주요 차이점 Task.WaitAllTask.WhenAll전 의지 블록 (하여 유사한이다 Wait후자 동안 단일 작업에서)이 모든 작업이 완료 될 때까지 호출자에게 제어를 다시 산출 기다리게 될 수 없다.

따라서 예외 처리는 다릅니다.

Task.WaitAll:

하나 이상의 Task 인스턴스가 취소되었거나 하나 이상의 Task 인스턴스를 실행하는 동안 예외가 발생했습니다. 작업이 취소 된 경우 AggregateException에는 InnerExceptions 컬렉션에 OperationCanceledException이 포함됩니다.

Task.WhenAll:

제공된 작업 중 하나가 오류 상태에서 완료되면 반환 된 작업도 오류 상태에서 완료됩니다. 여기에는 예외에 제공된 각 작업의 래핑되지 않은 예외 집합이 포함됩니다.

제공된 작업 중 하나라도 오류가 발생했지만 그 중 하나 이상이 취소 된 경우 반환 된 작업은 취소됨 상태로 종료됩니다.

오류가 발생한 작업이없고 취소 된 작업이 없으면 결과 작업은 RanToCompletion 상태로 종료됩니다. 제공된 배열 / 열거 가능한 작업이없는 경우 반환 된 작업은 호출자에게 반환되기 전에 즉시 RanToCompletion 상태로 전환됩니다.


답변

다음과 같은 많은 작업을 만들 수 있습니다.

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();
});


답변

당신은 사용할 수 있습니다 WhenAllawaitable 반환하는 Task또는 WaitAll더 리턴 유형이없는과에 더 코드 실행 시뮬을 차단합니다 Thread.Sleep모든 작업이 완료 취소 또는 폴트 때까지.

여기에 이미지 설명을 입력하십시오

var tasks = new Task[] {
    TaskOperationOne(),
    TaskOperationTwo()
};

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);

당신이 praticular 순서로 작업을 실행하려는 경우 양식 영감을 얻을 수있는 anwser를.


답변

Tasks 를 체인으로 연결 하시겠습니까 , 아니면 병렬로 호출 할 수 있습니까?

체인의 경우
그냥 그런 짓을

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
}