Pierwsze podejście: problem wszystko prosi jeden po drugim, a następnie czeka, aż wszystkie żądania wrócą, a następnie odfiltruje wynik. (Kod svick też to zrobił, ale tutaj robię to bez pośredniego ConcurrentQueue).
// First approach: massive fan-out
var tasks = addresses.Select(async a => new { A = a, C = await MeetsCriteriaAsync(a) });
var addressesAndCriteria = await Task.WhenAll(tasks);
var filteredAddresses = addressAndCriteria.Where(ac => ac.C).Select(ac => ac.A);
Drugie podejście: wykonywać żądania jeden po drugim. To potrwa dłużej, ale będzie to upewnij się, aby nie uderzać się usługa z ogromnym naporem żądań (zakładając, że MeetsCriteriaAsync wychodzi do usługa ...)
// Second approach: one by one
var filteredAddresses = new List<Uri>();
foreach (var a in filteredAddresses)
{
if (await MeetsCriteriaAsync(a)) filteredAddresses.Add(a);
}
Trzecie podejście: jak na sekundę, ale przy użyciu hipotetyczna funkcja C# 8 "strumienie asynchroniczne". C# 8 nie jest jeszcze dostępny, a strumienie asynchroniczne nie są jeszcze zaprojektowane, ale możemy marzyć! Typ IAsyncEnumerable już istnieje w RX i mam nadzieję, że dodadzą do niego więcej kombinatorów.Zaletą IAsyncEnumerable jest to, że możemy zacząć zużywać kilka pierwszych przefiltrowanych adresów, gdy tylko nadejdą, zamiast czekać, aż wszystko zostanie najpierw przefiltrowane.
// Third approach: ???
IEnumerable<Uri> addresses = {...};
IAsyncEnumerable<Uri> filteredAddresses = addresses.WhereAsync(MeetsCriteriaAsync);
czwarte podejście: może nie chcemy wbijać się usługa z wszystkich żądań naraz, ale jesteśmy szczęśliwi wydać więcej niż jeden wniosek na raz. Może przeprowadziliśmy eksperymenty i okazało się, że "trzy na raz" były szczęśliwym medium. UWAGA: ten kod zakłada jedno-wątkowy kontekst wykonania, na przykład w programowaniu interfejsu użytkownika lub ASP.NET. Jeśli jest uruchamiany w wielowątkowym kontekście wykonania, potrzebuje zamiast tego ConcurrentQueue i ConcurrentList.
// Fourth approach: throttle to three-at-a-time requests
var addresses = new Queue<Uri>(...);
var filteredAddresses = new List<Uri>();
var worker1 = FilterAsync(addresses, filteredAddresses);
var worker2 = FilterAsync(addresses, filteredAddresses);
var worker3 = FilterAsync(addresses, filteredAddresses);
await Task.WhenAll(worker1, worker2, worker3);
async Task FilterAsync(Queue<Uri> q, List<Uri> r)
{
while (q.Count > 0)
{
var item = q.Dequeue();
if (await MeetsCriteriaAsync(item)) r.Add(item);
}
}
Istnieją sposoby na czwarte podejście z wykorzystaniem biblioteki przepływu danych TPL.
Czego można się spodziewać, gdyby zostało to zasłonięte? Zwłaszcza podczas iteracji 'filtersAddresses', która jest po wywołaniu' MeetsCriteria'. –
@DanielHilgarth: Dzięki; trafne spostrzeżenie. To naprawdę nie pasuje do LINQ. – Sam