Pregunta

Considere este problema:Tengo un programa que debería recuperar (digamos) 100 registros de una base de datos y luego, para cada uno, debería obtener información actualizada de un servicio web.Hay dos formas de introducir paralelismo en este escenario:

  1. Comienzo cada solicitud al servicio web en un nuevo hilo.El número de subprocesos simultáneos está controlado por algún parámetro externo (o ajustado dinámicamente de alguna manera).

  2. Creo lotes más pequeños (digamos de 10 registros cada uno) y lanzo cada lote en un hilo separado (tomando nuestro ejemplo, 10 hilos).

¿Cuál es un mejor enfoque y por qué lo cree así?

¿Fue útil?

Solución

La opción 3 es la mejor:

Utilice E/S asíncrono.

A menos que el procesamiento de su solicitud sea complejo y pesado, su programa pasará el 99% de su tiempo esperando las solicitudes HTTP.

Esto es exactamente para lo que está diseñado Async IO: deje que la pila de redes de Windows (o .net framework o lo que sea) se preocupe por toda la espera y simplemente use un solo subproceso para enviar y "recoger" los resultados.

Desafortunadamente, el marco .NET lo convierte en un verdadero dolor de cabeza.Es más fácil si solo usa sockets sin formato o la API de Win32.Aquí hay un ejemplo (¡probado!) usando C#3 de todos modos:

using System.Net; // need this somewhere

// need to declare an class so we can cast our state object back out
class RequestState {
    public WebRequest Request { get; set; }
}

static void Main( string[] args ) {
    // stupid cast neccessary to create the request
    HttpWebRequest request = WebRequest.Create( "http://www.stackoverflow.com" ) as HttpWebRequest;

    request.BeginGetResponse(
        /* callback to be invoked when finished */
        (asyncResult) => { 
            // fetch the request object out of the AsyncState
            var state = (RequestState)asyncResult.AsyncState; 
            var webResponse = state.Request.EndGetResponse( asyncResult ) as HttpWebResponse;

            // there we go;
            Debug.Assert( webResponse.StatusCode == HttpStatusCode.OK ); 

            Console.WriteLine( "Got Response from server:" + webResponse.Server );
        },
        /* pass the request through to our callback */
        new RequestState { Request = request }  
    );

    // blah
    Console.WriteLine( "Waiting for response. Press a key to quit" );
    Console.ReadKey();
}

EDITAR:

En el caso de .NET, la 'devolución de llamada de finalización' en realidad se activa en un subproceso ThreadPool, no en su subproceso principal, por lo que aún necesitará bloquear todos los recursos compartidos, pero aún así le ahorrará todos los problemas de administrar subprocesos.

Otros consejos

Dos cosas a considerar.

1.¿Cuánto tiempo llevará procesar un registro?

Si el procesamiento de registros es muy rápido, la sobrecarga de pasar registros a subprocesos puede convertirse en un cuello de botella.En este caso, querrás agrupar los registros para no tener que entregarlos con tanta frecuencia.

Si el procesamiento de registros es razonablemente largo, la diferencia será insignificante, por lo que el enfoque más simple (1 registro por hilo) es probablemente el mejor.

2.¿Cuántos hilos planeas iniciar?

Si no está utilizando un grupo de subprocesos, creo que debe limitar manualmente la cantidad de subprocesos o dividir los datos en partes grandes.Iniciar un nuevo hilo para cada registro hará que su sistema funcione mal si la cantidad de registros aumenta.

La computadora que ejecuta el programa probablemente no sea el cuello de botella, por lo tanto:Recuerde que el protocolo HTTP tiene un encabezado keep-alive, que le permite enviar varias solicitudes GET en los mismos sockets, lo que le evita el apretón de manos de TCP/IP.Lamentablemente, no sé cómo usarlo en las bibliotecas .net.(Debería ser posible).

Probablemente también habrá un retraso en la respuesta a sus solicitudes.Puede intentar asegurarse de tener siempre un número determinado de solicitudes pendientes al servidor.

Consigue el efectos paralelos.Mire la Colección Blocking.Utilice un subproceso para alimentarlo con lotes de registros y de 1 a n subprocesos extrayendo registros de la colección para su servicio.Puede controlar la velocidad a la que se alimenta la colección y la cantidad de subprocesos que llaman a los servicios web.Hágalo configurable a través de ConfigSection y hágalo genérico alimentando la colección de delegados de acción, y tendrá un pequeño y agradable loter que podrá reutilizar a su gusto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top