la risposta di i3arnon con TPL Dataflow è buona; Dataflow è utile soprattutto se si dispone di un mix di CPU e codice associato I/O. Risponderò al suo sentimento che lo Parallel
è progettato per il codice associato alla CPU; non è la soluzione migliore per il codice basato su I/O e in particolare non appropriato per il codice asincrono.
Se si desidera una soluzione alternativa che funziona bene con la maggior parte-I Codice/O - e non richiede una libreria esterna - il metodo che stai cercando è Task.WhenAll
:
var tasks = uris.Select(uri => SendRequestAsync(uri)).ToArray();
await Task.WhenAll(tasks);
Questa è la soluzione più semplice, ma ha lo svantaggio di avviare tutte le richieste contemporaneamente. Soprattutto se tutte le richieste stanno per lo stesso servizio (o un piccolo insieme di servizi), questo può causare timeout. Per risolvere questo, è necessario utilizzare una sorta di strozzatura ...
Esiste una funzionalità (come il partizionatore TPL) che controlla il numero di attività massime e massimo HttpClient che posso creare?
TPL Dataflow ha quel bel MaxDegreeOfParallelism
che inizia solo così tanti alla volta. È inoltre possibile limitare codice asincrono regolare utilizzando un altro incorporato, SemaphoreSlim
:
private readonly SemaphoreSlim _sem = new SemaphoreSlim(50);
private async Task SendRequestAsync(Uri uri)
{
await _sem.WaitAsync();
try
{
...
}
finally
{
_sem.Release();
}
}
Nel caso di utilizzo di Task, invece, qual è la migliore pratica per la creazione di numero enorme di loro? Diciamo che uso Task.Factory.StartNew() e aggiungo quelle attività a un elenco e aspetto tutte.
In realtà non si desidera utilizzare StartNew
. Ha solo un caso d'uso appropriato (parallelismo basato su attività dinamiche), che è estremamente raro. Il codice moderno dovrebbe usare Task.Run
se devi spingere il lavoro su un thread in background. Ma non hai nemmeno bisogno di quello per cominciare, quindi né StartNew
né Task.Run
è appropriato qui.
Ci sono un paio di domande simili su SO, ma nessuno menziona i massimi. Il requisito è solo utilizzando le attività massime con HttpClient massimo.
I massimi sono i casi in cui il codice asincrono diventa davvero complicato. Con il codice (parallelo) collegato alla CPU, la soluzione è ovvia: si utilizzano tanti thread quanti sono i core. (Bene, almeno è possibile avviare lì e regolare se necessario). Con il codice asincrono, non c'è una soluzione altrettanto ovvia. Dipende da molti fattori: quanta memoria hai, come risponde il server remoto (limitazione di velocità, timeout, ecc.)
Non ci sono soluzioni facili qui. Devi solo testare come la tua applicazione specifica si occupa di alti livelli di concorrenza, e quindi limitare ad un numero inferiore.
Ho alcuni slides for a talk che tenta di spiegare quando diverse tecnologie sono appropriati (parallelismo, asincronia, TPL flusso di dati, e Rx). Se si preferisce più di una descrizione scritta con ricette, penso che si possa beneficiare di my book sulla concorrenza.
E se il numero di richieste simultanee supera il numero massimo di sistema operativo di richiesta http può fare? – ozgur
@ozgur Dipende da dove è configurato quel limite. Ma se ce n'è uno allora assicurati di impostare 'MaxDegreeOfParallelism' su qualcosa di più basso di quello. – i3arnon
Ultima domanda. L'esempio che hai fornito è adatto per le operazioni di I/O, ma non richiede il parallelismo della CPU? – ozgur