Tengo esto:
Parallel.ForEach(numbers, (number) => { var value = Regex.Replace(number, @"\s+", "%20"); tasks.Add(client.GetAsync(url + value)); }); await Task.WhenAll(tasks).ConfigureAwait(false); foreach (var task in tasks) { ... }A veces, devuelve menos tareas cuando se llega a foreach (var tarea en tareas), pero después de algunas solicitudes, comienza a devolver todas las tareas.
Cambié ConfigureAwait a verdadero y todavía a veces devuelve menos tareas.
Por cierto, estoy usando Parallel.ForEach porque cada cliente.GetAsync (url + valor) es una solicitud a una API externa con la particularidad de que su SLA de latencia es inferior a 1 s para el 99% de sus solicitudes
¿Pueden explicarme por qué a veces devuelve menos tareas?
¿Y hay alguna forma de garantizar la devolución siempre de todas las tareas?
Gracias
¿Y hay alguna forma de garantizar la devolución siempre de todas las tareas?
Varias personas en los comentarios señalan que solo debe hacer esto, suponiendo que los numbers no son una lista segura para subprocesos:
foreach(var number in numbers) { var value = Regex.Replace(number, @"\s+", "%20"); tasks.Add(client.GetAsync(url + value)); } await Task.WhenAll(tasks).ConfigureAwait(false); foreach (var task in tasks) { ... } No parece haber ningún beneficio considerable en paralelizar la creación de las tareas que realizan la descarga; esto sucede muy rápido. La espera a que se completen las descargas se realiza en el WhenAll
PD; hay una variedad de formas más complicadas de escapar datos para una URL, pero si está buscando específicamente convertir cualquier tipo de espacio en blanco a% 20, supongo que tiene sentido hacerlo con expresiones regulares.
Editar; usted preguntó cuándo usar un Parallel ForEach, y voy a decir "no lo haga, en general, porque debe tener más cuidado con los contextos en los que lo usa", pero si hizo Parallel.ForEach hacer más trabajo sincrónico, podría tener sentido:
Parallel.ForEach(numbers, number => { var value = Regex.Replace(number, @"\s+", "%20"); var r = client.Get(url + value)); //do something meaningful with r here, ie whatever ... is in your foreach (var task in tasks) });pero tenga en cuenta que si está realizando actualizaciones de algo compartido, con fines de coordinación, desde dentro del cuerpo, entonces deberá ser seguro para subprocesos
No lo ha mostrado, por lo que solo podemos adivinar, pero asumo que tasks son una List<> . Este tipo de colección no es seguro para subprocesos ; es probable que su ciclo paralelo "sobrescriba" valores. Realice un bloqueo manual de su lista o cambie a una colección segura para subprocesos, como ConcurrentQueue<>
var tasks = new ConcurrentQueue<Task<string>>(); Parallel.ForEach(numbers, number => { var value = Regex.Replace(number, @"\s+", "%20"); tasks.Enqueue(client.GetAsync(url + value)); }); await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); foreach (var task in tasks) { // whatever } Dicho esto, su uso de Parallel.ForEach es bastante sospechoso. No está realizando nada de importancia real dentro del bucle. El uso de Parallel , especialmente con el bloqueo adecuado, probablemente tenga una sobrecarga más alta que anule cualquier ganancia potencial que afirma observar o que se obtiene al paralelizar las llamadas Regex . Convertiría esto en un bucle foreach normal y precompilaría Regex para compensar (algo de) su sobrecarga:
// in class private static readonly Regex SpaceRegex = new Regex(@"\s+", RegexOptions.Compiled); // in method var tasks = new List<Task<string>>(); foreach (var number in numbers) { var value = SpaceRegex.Replace(number, "%20"); tasks.Add(client.GetAsync(url + value)); } await Task.WhenAll(tasks).ConfigureAwait(false); foreach (var task in tasks) { // whatever }Alternativamente, no use una expresión regular en absoluto. Use un mecanismo de escape Uri adecuado que tendrá el beneficio adicional de arreglar más que solo espacios:
var value = Uri.EscapeDataString(number); // or var fullUri = Uri.EscapeUriString(url + number); Tenga en cuenta que hay dos métodos diferentes allí. El adecuado para usar depende de los valores de url y number . También hay otros mecanismos como el método HttpUtility.UrlEncode ... pero creo que estos son los preferidos.