Domanda

Considera questo problema:Ho un programma che dovrebbe recuperare (diciamo) 100 record da un database, e quindi per ognuno dovrebbe ottenere informazioni aggiornate da un servizio web.Esistono due modi per introdurre il parallelismo in questo scenario:

  1. Inizio ogni richiesta al servizio web su un nuovo thread.Il numero di thread simultanei è controllato da alcuni parametri esterni (o regolati dinamicamente in qualche modo).

  2. Creo batch più piccoli (diciamo di 10 record ciascuno) e avvio ciascun batch su un thread separato (quindi prendendo il nostro esempio, 10 thread).

Qual è l’approccio migliore e perché la pensi così?

È stato utile?

Soluzione

L'opzione 3 è la migliore:

Utilizza I/O asincrono.

A meno che l'elaborazione delle richieste non sia complessa e pesante, il tuo programma trascorrerà il 99% del tempo in attesa delle richieste HTTP.

Questo è esattamente ciò per cui Async IO è progettato: lascia che lo stack di rete Windows (o .net framework o altro) si preoccupi di tutta l'attesa e utilizza semplicemente un singolo thread per inviare e "raccogliere" i risultati.

Sfortunatamente il framework .NET lo rende un vero rompicoglioni.È più semplice se usi solo socket grezzi o l'API Win32.Ecco un esempio (testato!) che utilizza comunque C#3:

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();
}

MODIFICARE:

Nel caso di .NET, il "callback di completamento" viene effettivamente attivato in un thread ThreadPool, non nel thread principale, quindi dovrai comunque bloccare eventuali risorse condivise, ma ti risparmia comunque tutti i problemi di gestione dei thread.

Altri suggerimenti

Due cose da considerare.

1.Quanto tempo ci vorrà per elaborare un record?

Se l'elaborazione dei record è molto rapida, il sovraccarico derivante dalla consegna dei record ai thread può diventare un collo di bottiglia.In questo caso, dovresti raggruppare i record in modo da non doverli consegnare così spesso.

Se l'elaborazione dei record ha una durata ragionevolmente lunga, la differenza sarà trascurabile, quindi l'approccio più semplice (1 record per thread) è probabilmente il migliore.

2.Quanti thread hai intenzione di iniziare?

Se non stai utilizzando un pool di thread, penso che sia necessario limitare manualmente il numero di thread oppure suddividere i dati in grandi blocchi.L'avvio di un nuovo thread per ogni record lascerà il sistema in difficoltà se il numero di record aumenta.

Il computer che esegue il programma probabilmente non è il collo di bottiglia, quindi:Ricorda che il protocollo HTTP ha un'intestazione keep-alive, che ti consente di inviare diverse richieste GET sugli stessi socket, il che ti evita il tremolio della mano TCP/IP.Sfortunatamente non so come usarlo nelle librerie .net.(Dovrebbe essere possibile.)

Probabilmente ci sarà anche un ritardo nella risposta alle vostre richieste.Potresti provare ad assicurarti di avere sempre un determinato numero di richieste in sospeso sul server.

Ottenere il Effetti paralleli.Guarda la BlockingCollection.Utilizza un thread per alimentarlo con batch di record e da 1 a n thread che estraggono i record dalla raccolta per metterli in servizio.Puoi controllare la velocità con cui viene alimentata la raccolta e il numero di thread che chiamano i servizi web.Rendilo configurabile tramite una ConfigSection e rendilo generico alimentando la raccolta Action delegates e avrai un bel batcher che potrai riutilizzare a tuo piacimento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top