여러 비동기 작업을 실행하고 모든 작업이 완료될 때까지 대기
콘솔 응용 프로그램에서 여러 비동기 작업을 실행하고 모든 작업이 완료될 때까지 기다렸다가 처리해야 합니다.
기사가 많은데 읽을수록 헷갈리는 것 같아요.작업 라이브러리의 기본 원칙을 읽고 이해했지만 어딘가에 링크가 없는 것이 분명합니다.
다른 작업이 완료된 후에 시작되도록 작업을 체인으로 연결할 수 있다는 것은 이해하지만(지금까지 읽은 모든 기사의 시나리오와 거의 동일), 모든 작업을 동시에 실행하고 모든 작업이 완료되면 알고 싶습니다.
이와 같은 시나리오에 대한 가장 간단한 구현은 무엇입니까?
두 답변 모두 대기자에 대해 언급하지 않았습니다.
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
와의 주요 차이점Task.WhenAll전자가 차단한다는 것입니다(사용하는 것과 유사함).Wait하나의 작업으로), 후자는 기다릴 수 없고 기다릴 수 있으며, 모든 작업이 끝날 때까지 호출자에게 제어권을 돌려줍니다.
더 중요한 것은 예외 처리가 다르다는 점입니다.
Task.WaitAll:
작업 인스턴스 중 하나 이상이 취소되었거나 작업 인스턴스 중 하나 이상을 실행하는 동안 예외가 발생했습니다.작업이 취소된 경우 집계Exception에는 InnerExceptions 컬렉션에 OperationCancelledException이 포함되어 있습니다.
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());
대기자를 반환하는 것을 사용할 수 있습니다.Task또는 반환 유형이 없으며 모든 작업이 완료, 취소 또는 오류가 발생할 때까지 와 유사한 추가 코드 실행을 차단합니다.
WhenAll |
WaitAll |
|
|---|---|---|
| 제공된 작업 중 하나가 장애 상태에서 완료됨 | 장애 상태의 작업이 반환됩니다.예외에는 제공된 각 작업의 래핑되지 않은 예외 집합이 포함됩니다. | 던져질 거예요. |
| 제공된 작업 중 장애가 발생하지 않았지만 작업 중 하나 이상이 취소되었습니다. | 반환된 작업은 상태에서 종료됩니다. | 컬렉션에 포함된 A가 던져집니다. |
| 빈 목록이 제공되었습니다. | 던져질 것입니다. | 반환된 작업은 호출자에게 반환되기 전에 즉시 상태로 전환됩니다. |
| 현재 스레드를 차단하지 않습니다. | 현재 스레드를 차단합니다. |
예
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
작업을 특정/특정 순서로 실행하려면 이 답변에서 영감을 얻을 수 있습니다.
내가 본 최고의 옵션은 다음과 같은 확장 방법입니다.
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();
});
하지만 또 다른 대답은...하지만 데이터를 동시에 로드하고 변수에 넣어야 하는 경우가 많습니다. 예를 들어 다음과 같습니다.
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
}
체인을 연결하시겠습니까?Tasks, 또는 병렬 방식으로 호출할 수 있습니까?
그냥 뭐 그런 거 해봐요.
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
그리고 앞의 것을 확인하는 것을 잊지 마세요.Task 각의예에 인스턴스ContinueWith잘못이 있을지도 모르니
방식에 ▁for경.
제가 발견한 가장 간단한 방법은:Parallel.Invoke 그렇지 않으면 당신이 사용할 수도 있습니다.WaitHandles 남은 작업을 0으로 카운트다운하기 위해(잠깐, 새 클래스가 있습니다:) 또는 ...
이것이 배열 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
이러한 시나리오 중 일부에 대해 작업을 사용하는 방법을 보여줄 코드를 준비했습니다.
// 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);
}
받아들여진 답보다 더 간결한 해결책이 있어야 합니다.여러 작업을 동시에 실행하고 결과를 얻는 데는 세 단계가 필요하지 않습니다.
- 작업 만들기
- 작업을 기다립니다.모든 경우(작업)
- 작업 결과(예: task1)를 가져옵니다.결과)
이를 두 단계로 줄이는 방법은 다음과 같습니다.
public async Task<Tuple<T1, T2>> WhenAllGeneric<T1, T2>(Task<T1> task1, Task<T2> task2)
{
await Task.WhenAll(task1, task2);
return Tuple.Create(task1.Result, task2.Result);
}
다음과 같이 사용할 수 있습니다.
var taskResults = await Task.WhenAll(DoWorkAsync(), DoMoreWorkAsync());
var DoWorkResult = taskResults.Result.Item1;
var DoMoreWorkResult = taskResults.Result.Item2;
이렇게 하면 임시 작업 변수가 필요하지 않습니다.이 기능을 사용할 때의 문제는 두 가지 작업에 대해 작동하지만 세 가지 작업 또는 다른 여러 가지 작업에 대해 업데이트해야 한다는 것입니다.또한 작업 중 하나가 아무것도 반환하지 않으면 제대로 작동하지 않습니다.진짜로.Net 라이브러리는 이를 수행할 수 있는 무언가를 제공해야 합니다.
비동기/대기 패턴을 사용하는 경우 다음과 같이 여러 작업을 병렬로 실행할 수 있습니다.
public async Task DoSeveralThings()
{
// Start all the tasks
Task first = DoFirstThingAsync();
Task second = DoSecondThingAsync();
// Then wait for them to complete
var firstResult = await first;
var secondResult = await second;
}
언급URL : https://stackoverflow.com/questions/25009437/running-multiple-async-tasks-and-waiting-for-them-all-to-complete
'programing' 카테고리의 다른 글
| 하위 모듈을 최신 커밋으로 업데이트 (0) | 2023.05.04 |
|---|---|
| 32비트 전용 Microsoft Common Controls(ListView)를 대체할 방법을 찾고 있습니다. (0) | 2023.05.04 |
| 파일을 세는 bash 명령어가 있습니까? (0) | 2023.05.04 |
| 응답을 피하는 방법.종료() Excel 파일 다운로드 중 "스레드가 중단되었습니다." 예외 (0) | 2023.04.29 |
| Swift에서 유형이 다른 Superclass 속성을 재정의하는 중 (0) | 2023.04.29 |