Ho un'enumerazione di elementi (RunData.Demand
), ognuno dei quali rappresenta un'operazione che comporta la chiamata di un'API su HTTP. Funziona benissimo se ho solo foreach
attraverso tutto e chiama l'API durante ogni iterazione. Tuttavia, ogni iterazione richiede un secondo o due, quindi mi piacerebbe eseguire 2-3 thread e dividere il lavoro tra di loro. Ecco quello che sto facendo:Come accodare correttamente le attività da eseguire in C#
ThreadPool.SetMaxThreads(2, 5); // Trying to limit the amount of threads
var tasks = RunData.Demand
.Select(service => Task.Run(async delegate
{
var availabilityResponse = await client.QueryAvailability(service);
// Do some other stuff, not really important
}));
await Task.WhenAll(tasks);
La chiamata client.QueryAvailability
chiama fondamentalmente un'API utilizzando la classe HttpClient
:
public async Task<QueryAvailabilityResponse> QueryAvailability(QueryAvailabilityMultidayRequest request)
{
var response = await client.PostAsJsonAsync("api/queryavailabilitymultiday", request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<QueryAvailabilityResponse>();
}
throw new HttpException((int) response.StatusCode, response.ReasonPhrase);
}
Questa grande opera per un po ', ma alla fine le cose cominciano timeout. Se imposto il timeout di HttpClient a un'ora, inizierò a ricevere strani errori del server interno.
Quello che ho iniziato a fare era impostare un cronometro nel metodo QueryAvailability
per vedere cosa stava succedendo.
Quello che sta accadendo è che tutti i 1200 elementi in RunData.Demand vengono creati contemporaneamente e tutti i 1200 metodi await client.PostAsJsonAsync
vengono chiamati. Sembra quindi che usi i 2 thread per ricontrollare lentamente le attività, quindi verso la fine ho compiti che sono stati in attesa di 9 o 10 minuti.
Ecco il comportamento vorrei:
Mi piacerebbe creare i 1.200 compiti, poi eseguirli 3-4 alla volta come le discussioni diventano disponibili. Io faccio non voglio fare la coda di 1.200 chiamate HTTP immediatamente.
C'è un buon modo per fare questo?
Non sembra che tu crei un nuovo 'client' per ogni chiamata. Sai che 'System.Net.Http.HttpClient' non è thread-safe per le chiamate di istanza? Dovrebbe essere creata una nuova istanza per (e smaltita dopo) ogni chiamata. – Enigmativity
Il metodo 'QueryAvailability' è in una classe che crea' HttpClient', che è un membro privato di quell'istanza. Non sapevo che non fosse thread-safe, potevo sicuramente crearlo prima di ogni chiamata. Lo esaminerò di più, grazie! –
Hmm, ho fatto un po 'di ricerche e sembra che quello che sto facendo sia sicuro. Vedi [qui] (http://stackoverflow.com/questions/11178220/is-httpclient-safe-to-use-concurrently) e [here] (http://www.tomdupont.net/2014/11/net- 45-httpclient-is-thread-safe.html) –