Primo approccio: problema tutto chiede in anticipo uno dopo l'altro, quindi attendi fino a quando tutte le richieste non tornano, quindi filtra il risultato. (Anche il codice di svick ha fatto questo, ma qui lo sto facendo senza il ConcurrentQueue intermedio).
// 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);
Secondo approccio: eseguire le richieste una dopo l'altra. Questo richiederà più tempo ma si farà in modo di non martellare il webservice con un enorme attacco di richieste (supponendo che MeetsCriteriaAsync va a un webservice ...)
// Second approach: one by one
var filteredAddresses = new List<Uri>();
foreach (var a in filteredAddresses)
{
if (await MeetsCriteriaAsync(a)) filteredAddresses.Add(a);
}
Terzo approccio: come per il secondo, ma usando un ipotetico C# 8 presenta "flussi asincroni". Il C# 8 non è ancora uscito, e gli stream asincroni non sono ancora stati progettati, ma possiamo sognare! Il tipo IAsyncEnumerable esiste già in RX e si spera che aggiungano altri combinatori per questo.La cosa bella di IAsyncEnumerable è che possiamo iniziare a consumare i primi filteredAddress non appena arrivano, piuttosto che aspettare che tutto venga filtrato per primo.
// Third approach: ???
IEnumerable<Uri> addresses = {...};
IAsyncEnumerable<Uri> filteredAddresses = addresses.WhereAsync(MeetsCriteriaAsync);
Quarto approccio: forse noi non vogliamo martello il webservice con tutte le richieste tutti in una volta, ma siamo felici di emettere più di una richiesta alla volta. Forse abbiamo fatto esperimenti e abbiamo scoperto che "tre alla volta" era un mezzo felice. NOTA: questo codice presuppone un contesto di esecuzione a thread singolo come nella programmazione dell'interfaccia utente o ASP.NET. Se viene eseguito in un contesto di esecuzione multithread, è necessario un ConcurrentQueue e 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);
}
}
Ci sono modi per il quarto approccio utilizzando anche la libreria di flusso di dati TPL.
Cosa ti aspetti che accadrebbe se questo fosse supportato? Soprattutto quando iterating 'filteredAddresses' che è quando viene chiamato' MeetsCriteria'. –
@DanielHilgarth: Grazie; è un buon punto. Questo non sembra adattarsi a LINQ. – Sam