Pergunta

Considere este problema:Eu tenho um programa que deve buscar (digamos) 100 registros de um banco de dados e, para cada um, obter informações atualizadas de um serviço web.Existem duas maneiras de introduzir paralelismo neste cenário:

  1. Eu inicio cada solicitação ao serviço web em um novo Thread.O número de threads simultâneos é controlado por algum parâmetro externo (ou ajustado dinamicamente de alguma forma).

  2. Eu crio lotes menores (digamos de 10 registros cada) e lanço cada lote em um thread separado (tomando nosso exemplo, 10 threads).

Qual é a melhor abordagem e por que você acha isso?

Foi útil?

Solução

A opção 3 é a melhor:

Use IO assíncrono.

A menos que o processamento de sua solicitação seja complexo e pesado, seu programa gastará 99% do tempo aguardando as solicitações HTTP.

É exatamente para isso que o Async IO foi projetado - deixe a pilha de rede do Windows (ou estrutura .net ou qualquer outra coisa) se preocupar com toda a espera e apenas usar um único thread para despachar e 'captar' os resultados.

Infelizmente, o framework .NET torna isso um verdadeiro pé no saco.É mais fácil se você estiver usando apenas soquetes brutos ou a API Win32.Aqui está um exemplo (testado!) Usando C#3 de qualquer maneira:

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:

No caso do .NET, o 'retorno de chamada de conclusão' na verdade é acionado em um thread ThreadPool, não em seu thread principal, portanto, você ainda precisará bloquear todos os recursos compartilhados, mas ainda assim evita todo o trabalho de gerenciamento de threads.

Outras dicas

Duas coisas a considerar.

1.Quanto tempo levará para processar um registro?

Se o processamento de registros for muito rápido, a sobrecarga de transferência de registros para threads pode se tornar um gargalo.Nesse caso, você gostaria de agrupar os registros para não precisar entregá-los com tanta frequência.

Se o processamento de registros for razoavelmente demorado, a diferença será insignificante; portanto, a abordagem mais simples (1 registro por thread) é provavelmente a melhor.

2.Quantos tópicos você planeja iniciar?

Se você não estiver usando um pool de threads, acho que você precisa limitar manualmente o número de threads ou dividir os dados em grandes partes.Iniciar um novo thread para cada registro deixará seu sistema em debate se o número de registros aumentar.

O computador que executa o programa provavelmente não é o gargalo, então:Lembre-se de que o protocolo HTTP possui um cabeçalho keep-alive, que permite enviar diversas solicitações GET nos mesmos soquetes, o que evita o aperto de mão do TCP/IP.Infelizmente não sei como usar isso nas bibliotecas .net.(Deve ser possível.)

Provavelmente também haverá um atraso na resposta às suas solicitações.Você pode tentar garantir que sempre tenha um determinado número de solicitações pendentes ao servidor.

Pegue o FX paralelo.Veja o BlockingCollection.Use um thread para alimentá-lo com lotes de registros e 1 a n threads puxando registros da coleção para o serviço.Você pode controlar a taxa na qual a coleção é alimentada e o número de threads que chamam serviços web.Torne-o configurável por meio de um ConfigSection e torne-o genérico alimentando a coleção de delegados de ação, e você terá um pequeno loteador que pode reutilizar como desejar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top