programing

여러 비동기 작업을 실행하고 모든 작업이 완료될 때까지 대기

javajsp 2023. 5. 4. 18:39

여러 비동기 작업을 실행하고 모든 작업이 완료될 때까지 대기

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

기사가 많은데 읽을수록 헷갈리는 것 같아요.작업 라이브러리의 기본 원칙을 읽고 이해했지만 어딘가에 링크가 없는 것이 분명합니다.

다른 작업이 완료된 후에 시작되도록 작업을 체인으로 연결할 수 있다는 것은 이해하지만(지금까지 읽은 모든 기사의 시나리오와 거의 동일), 모든 작업을 동시에 실행하고 모든 작업이 완료되면 알고 싶습니다.

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

두 답변 모두 대기자에 대해 언급하지 않았습니다.

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

받아들여진 답보다 더 간결한 해결책이 있어야 합니다.여러 작업을 동시에 실행하고 결과를 얻는 데는 세 단계가 필요하지 않습니다.

  1. 작업 만들기
  2. 작업을 기다립니다.모든 경우(작업)
  3. 작업 결과(예: 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