linq select에서 비동기식 대기
기존 프로그램을 수정해야하며 다음 코드가 포함되어 있습니다.
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
그러나이 먼저 모든 사용, 나에게 매우 이상한 것 같습니다 async
과 await
선택에. Stephen Cleary 의이 답변 에 따르면 나는 그것들을 떨어 뜨릴 수 있어야합니다.
그런 다음 두 번째 Select
결과를 선택합니다. 이것은 작업이 전혀 비동기 적이 지 않고 동 기적으로 수행되므로 (아무것도 많은 노력을 기울이지 않음), 작업이 비동기 적으로 수행되고 완료되면 나머지 쿼리가 실행되는 것을 의미하지 않습니까?
나는에 따라 다음과 같이 위의 코드를 작성해야 스티븐 클리 어리하여 다른 답변 :
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
그리고 이것과 완전히 동일합니까?
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
이 프로젝트에서 작업하는 동안 첫 번째 코드 샘플을 변경하고 싶지만 비동기 코드 변경 (거의 작동)에 너무 열중하지 않습니다. 어쩌면 나는 아무것도 걱정하지 않고 3 개의 코드 샘플이 모두 똑같은 일을합니까?
ProcessEventsAsync는 다음과 같습니다.
async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
그러나 이것은 나에게 매우 이상하게 보입니다. 먼저 async를 사용하고 선택을 기다립니다. Stephen Cleary 의이 답변에 따르면 나는 그것들을 떨어 뜨릴 수 있어야합니다.
에 대한 호출 Select
이 유효합니다. 이 두 줄은 기본적으로 동일합니다.
events.Select(async ev => await ProcessEventAsync(ev))
events.Select(ev => ProcessEventAsync(ev))
(에서 동기식 예외가 발생하는 방법과는 약간의 차이가 ProcessEventAsync
있지만이 코드의 맥락에서는 전혀 중요하지 않습니다.)
그런 다음 결과를 선택하는 두 번째 선택. 이것은 작업이 전혀 비동기 적이 지 않고 동 기적으로 수행되므로 (아무것도 많은 노력을 기울이지 않음), 작업이 비동기 적으로 수행되고 완료되면 나머지 쿼리가 실행되는 것을 의미하지 않습니까?
이는 쿼리가 차단되고 있음을 의미합니다. 따라서 실제로 비동기 적이 지 않습니다.
세분화 :
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
먼저 각 이벤트에 대해 비동기 작업을 시작합니다. 그런 다음이 줄 :
.Select(t => t.Result)
해당 작업이 한 번에 하나씩 완료 될 때까지 기다립니다 (먼저 첫 번째 이벤트의 작업을 기다린 후 다음, 다음에 다음 등).
이 부분은에서 예외를 차단하고 래핑하기 때문에 내가 신경 쓰지 않는 부분입니다 AggregateException
.
그리고 이것과 완전히 동일합니까?
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
예,이 두 예는 동일합니다. 둘 다 모든 비동기 작업을 시작한 events.Select(...)
다음 ( ) 모든 작업이 임의의 순서로 완료 될 때까지 비동기 적으로 기다린 다음 ( await Task.WhenAll(...)
) 나머지 작업을 진행합니다 ( Where...
).
이 두 예제는 원래 코드와 다릅니다. 원래 코드가 차단되어에서 예외를 래핑 AggregateException
합니다.
기존 코드는 작동하지만 스레드를 차단하고 있습니다.
.Select(async ev => await ProcessEventAsync(ev))
모든 이벤트에 대해 새 작업을 생성하지만
.Select(t => t.Result)
새 작업이 끝날 때까지 기다리는 스레드를 차단합니다.
반면에 코드는 동일한 결과를 생성하지만 비동기 적으로 유지합니다.
첫 번째 코드에 대한 하나의 주석. 이 라인
var tasks = await Task.WhenAll(events...
단일 작업을 생성하므로 변수의 이름을 단수로 지정해야합니다.
마지막으로 마지막 코드는 동일하지만 간결합니다.
For reference: Task.Wait / Task.WhenAll
With current methods available in Linq it looks quite ugly:
var tasks = items.Select(
async item => new
{
Item = item,
IsValid = await IsValid(item)
});
var tuples = await Task.WhenAll(tasks);
var validItems = tuples
.Where(p => p.IsValid)
.Select(p => p.Item)
.ToList();
Hopefully following versions of .NET will come up with more elegant tooling to handle collections of tasks and tasks of collections.
I prefer this as an extension method:
public static async Task<IEnumerable<T>> WhenAll<T>(this IEnumerable<Task<T>> tasks)
{
return await Task.WhenAll(tasks);
}
So that it is usable with method chaining:
var inputs = await events
.Select(async ev => await ProcessEventAsync(ev))
.WhenAll()
I used this code:
public static async Task<IEnumerable<TResult>> SelectAsync<TSource,TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> method)
{
return await Task.WhenAll(source.Select(async s => await method(s)));
}
like this:
var result = await sourceEnumerable.SelectAsync(async s=>await someFunction(s,other params));
참고URL : https://stackoverflow.com/questions/35011656/async-await-in-linq-select
'Programming' 카테고리의 다른 글
스핑크스 오토 독은 충분히 자동적이지 않다 (0) | 2020.06.23 |
---|---|
Matplotlib 산점도; (0) | 2020.06.23 |
이전 커밋이 아닌 커밋 수정 (0) | 2020.06.23 |
Cloneable이 더 이상 사용되지 않는 이유는 무엇입니까? (0) | 2020.06.23 |
Javadoc의 좋은 예 (0) | 2020.06.23 |