다음과 같이 만든 작업 목록이 있습니다.
public async Task<IList<Foo>> GetFoosAndDoSomethingAsync()
{
var foos = await GetFoosAsync();
var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
...
}
를 사용 .ToList()
하면 작업이 모두 시작됩니다. 이제 완료를 기다리고 결과를 반환하고 싶습니다.
이것은 위의 ...
블록 에서 작동합니다 .
var list = new List<Foo>();
foreach (var task in tasks)
list.Add(await task);
return list;
내가 원하는 것을하지만 이것은 다소 서투른 것 같습니다. 나는 차라리 다음과 같이 더 간단한 것을 작성하고 싶습니다.
return tasks.Select(async task => await task).ToList();
…하지만 이것은 컴파일되지 않습니다. 내가 무엇을 놓치고 있습니까? 아니면 이런 식으로 표현하는 것이 불가능합니까?
답변
LINQ는 async
코드에서 완벽하게 작동하지 않지만 다음과 같이 할 수 있습니다.
var tasks = foos.Select(DoSomethingAsync).ToList();
await Task.WhenAll(tasks);
작업이 모두 동일한 유형의 값을 반환하는 경우 다음과 같이 할 수도 있습니다.
var results = await Task.WhenAll(tasks);
꽤 좋습니다. WhenAll
배열을 반환하므로 메서드가 결과를 직접 반환 할 수 있다고 생각합니다.
return await Task.WhenAll(tasks);
답변
Stephen의 답변을 확장하기 위해 LINQ 의 유창한 스타일 을 유지하기 위해 다음 확장 메서드를 만들었습니다 . 그런 다음 할 수 있습니다.
await someTasks.WhenAll()
namespace System.Linq
{
public static class IEnumerableExtensions
{
public static Task<T[]> WhenAll<T>(this IEnumerable<Task<T>> source)
{
return Task.WhenAll(source);
}
}
}
답변
Task.WhenAll의 한 가지 문제는 병렬 처리를 생성한다는 것입니다. 대부분의 경우 더 좋을 수도 있지만 때로는 피하고 싶을 때도 있습니다. 예를 들어, DB에서 일괄 데이터를 읽고 일부 원격 웹 서비스로 데이터를 전송합니다. 모든 배치를 메모리에로드하고 싶지는 않지만 이전 배치가 처리되면 DB에 도달합니다. 따라서 비동기 성을 깨야합니다. 다음은 그 예입니다.
var events = Enumerable.Range(0, totalCount/ batchSize)
.Select(x => x*batchSize)
.Select(x => dbRepository.GetEventsBatch(x, batchSize).GetAwaiter().GetResult())
.SelectMany(x => x);
foreach (var carEvent in events)
{
}
참고 .GetAwaiter (). GetResult ()는 동기식으로 변환합니다. 이벤트의 batchSize가 처리 된 후에 만 DB가 느리게 적중됩니다.
답변
Task.WaitAll
또는 Task.WhenAll
적절한 것을 사용하십시오 .
답변
Task.WhenAll이 여기서 트릭을해야합니다.