5

Estoy tratando de implementar .NET 4 helper/utility class que debería recuperar fuentes de páginas HTML basadas en la lista de URL para la herramienta webtesting. La solución debe ser escalable y tener un alto rendimiento.Cómo hacer multi-threading con webrequests asíncronas

He estado investigando y probando diferentes soluciones desde hace muchos días, pero no puedo encontrar la solución adecuada.

De acuerdo con mi comprensión, la mejor manera de lograr mi objetivo sería utilizar webrequests asíncronas que se ejecutan en paralelo utilizando TPL.

Para tener un control total de los encabezados, etc. Estoy usando HttpWebResponse en lugar de WebClient que está envolviendo HttpWebResponse. En algunos casos, el resultado debe estar encadenado a otras tareas, por lo que el uso de tareas TPL podría tener sentido.

Lo que he logrado hasta ahora después de muchos diferentes ensayos/enfoques,

  1. Implementado sincrónica básica, asíncrono (APM) y paralelo (utilizando tareas TPL) soluciones para ver el nivel de rendimiento de las diferentes soluciones.

  2. Para ver el rendimiento de la solución paralela asincrónica, utilicé el enfoque APM, BeginGetResponse y BeginRead, y lo ejecuté en Parallel.ForEach. Todo funciona bien y estoy contento con el rendimiento. De alguna manera, creo que usar el simple Parallel.ForEach no es el camino a seguir y, por ejemplo, no sé cómo usaría el encadenamiento de tareas.

  3. Luego probé un sistema más sofisticado usando tareas para envolver la solución APM utilizando TaskCompletionSource e iterador para iterar a través del flujo APM. Creo que esta solución podría ser lo que estoy buscando, pero hay un retraso extraño, algo entre 6 y 10 segundos, que ocurre 2-3 veces cuando se ejecuta la lista de 500 URL.

    Según los registros, la ejecución ha regresado al hilo que llama a la recuperación asincrónica en un bucle cuando ocurre la demora. La demora no ocurre siempre cuando la ejecución vuelve al ciclo, solo 2-3 veces, otras veces funciona bien. Parece que el hilo de bucle crearía un conjunto de tareas que serían procesadas por otros hilos y mientras la mayoría de las tareas se completen, habría demora (6-8s) antes de que el bucle continúe creando las tareas restantes y otros hilos estén activos nuevamente. .

El principio de iterador en el interior del bucle es:

IEnumerable<Task> DoExample(string input) 
    { 
    var aResult = DoAAsync(input); 
    yield return aResult; 
    var bResult = DoBAsync(aResult.Result); 
    yield return bResult; 
    var cResult = DoCAsync(bResult.Result); 
    yield return cResult; 
    … 
    } 

Task t = Iterate(DoExample(“42”)); 

estoy resolver el límite de conexión mediante el uso de System.Net.ServicePointManager.DefaultConnectionLimit y tiempo de espera utilizando ThreadPool.RegisterWaitForSingleObject

Mi pregunta es simplemente, ¿cuál sería el mejor enfoque para implementar la clase auxiliar/utilidad para recuperar páginas html que:

  • ser escalable y tienen un alto rendimiento
  • webrequests uso
  • ser de cadena fácilmente a otras tareas
  • poder utilizar tiempo de espera de uso
  • .NET 4 framework

Si crees que la solución de utilizar APM, TaskCompletionSource y el iterador, que presenté anteriormente, está bien, agradecería cualquier ayuda para tratar de resolver el problema de retraso.

Soy totalmente nuevo en el desarrollo de C# y Windows, así que no te preocupes si algo de lo que estoy probando no tiene demasiado sentido.

Cualquier ayuda sería muy apreciada, ya que sin resolver esto tengo que dejar el desarrollo de mi herramienta de prueba.

Gracias

+0

¿Podría explicar con más detalle cómo está utilizando el iterador y por qué cree que es útil tenerlo como un iterador? – svick

+0

Después de probar varias soluciones, terminé usando iteradores basados ​​en el asesoramiento experto de MS en msdn blog. Mi solución es más o menos la misma que en el blog, solo agregué tiempo de espera y registro. No tengo ninguna razón específica para usar iteradores y estoy abierto a cualquier solución que funcione. Enlace al fragmento de código: http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/95355648-1fa6-4b2d-a260-954c3421c453/ – Laowai

Respuesta

0

uso de iteradores fue una gran solución en la pre-TPL .NET (por ejemplo, el tiempo de ejecución de Coordinación y de concurrencia (CCR) de MS Robotics hace un gran uso de ellos y ayudó a inspirar TPL). Uno de los problemas es que los iteradores por sí solos no le proporcionarán lo que necesita: también necesita un planificador para distribuir de manera efectiva la carga de trabajo. Eso es casi realizado por fragmento de Stephen Toub que haya vinculado a - pero tenga en cuenta que una línea:

enumerator.Current.ContinueWith(recursiveBody, TaskContinuationOptions.ExecuteSynchronously); 

creo que los problemas intermitentes que estamos viendo podría estar vinculado a forzar "ExecuteSynchronously" - que podría ser la causa de una distribución desigual del trabajo en los núcleos/hilos disponibles.

Eche un vistazo a algunas de las otras alternativas que Stephen propone in his blog article. En particular, vea qué hará un simple encadenamiento de llamadas a ContinueWith() si es necesario, seguido de llamadas Unwrap() coincidentes. La sintaxis no será la más bonita, pero es la más simple e interfiere lo menos posible con el tiempo de ejecución de robo de trabajo subyacente, por lo que con suerte obtendrás mejores resultados.

+0

Gracias por sus sugerencias y comentarios. Daré una mirada más cercana al blog de Stephen. – Laowai

+0

¡Claro! Háganos saber lo que encuentre... –