2014-04-16 29 views
15

Aşağıdaki parçacık derleniyor, ancak bana bir List<Task<T>> vermek yerine görev sonucunu beklemesini beklerdim.Bir LINQ ifadesinin içinde async/bekletme kullanıldığında ne olur?

var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList(); 
await Task.WhenAll(tasks); 

Ama a commentSelect()async ve await gerekli olmadığını işaret:

var foo = bars.Select(async bar => await Baz(bar)).ToList() 

olarak, Task.WhenAll kullanmak gerekir dışarı here çekti

var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList(); 

Benzer bir here sorusu, birisinin içinde bir async yöntemini kullanmaya çalışır. Where(). Bir LINQ ifadesinin içinde async ve await ve ve await, yasal bir sözdizimidir, ancak hiçbir şey yapmaz mı, yoksa belirli bir kullanımı var mı?

cevap

21

Bunu "LINQ içinde async kullanarak" düşünmemenizi öneririz. İkisinin arasında ne olduğunu aklınızda bulundurun: delege. Birkaç LINQ operatörü delegeleri alır ve eşzamanlı olmayan bir temsilci oluşturmak için async kullanılabilir. Eğer uyumsuz bir yöntem BazAsync varken

Yani, bu döndüren

bir Task:

Task BazAsync(TBar bar); 

sonra görevlerin bir dizide bu kod sonuçları:

IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar)); 

Benzer şekilde, kullanmak durumunda async ve await delege içinde, bir Task:

döndüren bir zaman uyumsuz temsilci oluşturuyorsunuz 210 Bu iki LINQ ifadesi, işlevsel olarak eşdeğerdir. Önemli farklılıklar yoktur.

Düzenli LINQ ifadeleri gibi, IEnumerable<Task> da tembel olarak değerlendirilir. Yalnızca, BazAsync gibi eşzamansız yöntemlerle, genellikle yanlış tesadüfi çift değerlendirme veya bunun gibi bir şey yapmak istemezsiniz.Bu yüzden, bir dizi görev için projelendirdiğinizde, diziyi hemen hemen doğrulamak iyi bir fikirdir. Bu gidiş tüm görevleri başlangıç ​​kaynak dizideki tüm elemanlar için BazAsync çağırır: Elbette

Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray(); 

, biz Select ile yaptığım tüm her eleman için bir zaman uyumsuz işlem başlamaktır. Onlara tamamlamak için tüm beklemek isterseniz, o zaman Task.WhenAll kullanın:

await Task.WhenAll(tasks); 

Diğer birçok LINQ operatörleri asenkron delegelerle olarak temiz bir şekilde çalışmaz. Select oldukça basittir: her öğe için asenkronize bir işlem başlatıyorsunuz.

+0

"Önemli bir farklılık yok." - BazAsync eşzamanlı olarak atarsa, async olmayan lambda'yı kullanarak, numaralandırma sırasında ve async lambda'yı kullanarak gerçekleşir, görevler beklenirken gerçekleşir. –

+1

@EliArbel: Evet. Ancak eşzamanlı olarak atanan asenkron bir yöntem çok sıra dışı bir durumdur. Eşzamansız yöntemler * yalnızca * [gizli özel durumlar] durumunda senkronizasyon istisnaları atmalıdır (https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/). Bu yüzden önemli bir kullanım durumu düşünmüyorum. –

+1

reify - https://en.wikipedia.org/wiki/Reification_(computer_science) – BozoJoe

4

belli kullanacağım

Tabii var. Bir LINQ ifadesinin içinde async ve bekletme ile ör. Böyle bir şey yapmak: zaman uyumsuz olmadan

var tasks = foos.Select(async foo => 
    { 
     var intermediate = await DoSomethingAsync(foo); 
     return await DoSomethingElseAsync(intermediate); 
    }).ToList(); 
await Task.WhenAll(tasks); 

/LINQ deyimi İçine hiçbir şey beklemiyor olduğunuz bir LINQ deyimi içinde bekliyor, bu yüzden sonucu işleyemez, ya da başka bir şey için bekliyor.

Eşzamansız/beklemede, LINQ deyiminde yalnızca görevleri başlatıyorsunuz, ancak bunların tamamlanmasını beklemiyorsunuz. Sonunda yine de tamamlayacaklar, ancak kontrol LINQ ifadesinden ayrıldıktan sonra çok geçecek, böylece sonuçlara yalnızca WhenAll hattının tamamlanmasından sonra LINQ deyiminin tamamlanmasından sonra erişebilirsiniz.

İlgili konular