Question

Considérez ce problème :J'ai un programme qui devrait récupérer (disons) 100 enregistrements d'une base de données, puis pour chacun, il devrait obtenir des informations mises à jour à partir d'un service Web.Il existe deux manières d'introduire le parallélisme dans ce scénario :

  1. Je démarre chaque requête au service Web sur un nouveau fil de discussion.Le nombre de threads simultanés est contrôlé par un paramètre externe (ou ajusté dynamiquement d'une manière ou d'une autre).

  2. Je crée des lots plus petits (disons de 10 enregistrements chacun) et lance chaque lot sur un thread séparé (en reprenant donc notre exemple, 10 threads).

Quelle est la meilleure approche et pourquoi pensez-vous ainsi ?

Était-ce utile?

La solution

L'option 3 est la meilleure :

Utilisez les E/S asynchrones.

À moins que le traitement de vos requêtes ne soit complexe et lourd, votre programme passera 99 % de son temps à attendre les requêtes HTTP.

C'est exactement pour cela qu'Async IO est conçu : laissez la pile réseau Windows (ou le framework .net ou autre) se soucier de toute l'attente et utilisez simplement un seul thread pour distribuer et « récupérer » les résultats.

Malheureusement, le framework .NET en fait un vrai casse-tête.C'est plus facile si vous utilisez uniquement des sockets bruts ou l'API Win32.Voici un exemple (testé !) utilisant quand même 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();
}

MODIFIER:

Dans le cas de .NET, le « rappel d'achèvement » est en fait déclenché dans un thread ThreadPool, pas dans votre thread principal, vous devrez donc toujours verrouiller toutes les ressources partagées, mais cela vous évite toujours tous les problèmes de gestion des threads.

Autres conseils

Deux choses à considérer.

1.Combien de temps faudra-t-il pour traiter un dossier ?

Si le traitement des enregistrements est très rapide, la surcharge liée au transfert des enregistrements vers les threads peut devenir un goulot d'étranglement.Dans ce cas, vous souhaiterez regrouper les enregistrements afin de ne pas avoir à les transmettre si souvent.

Si le traitement des enregistrements dure raisonnablement longtemps, la différence sera négligeable, donc l'approche la plus simple (1 enregistrement par thread) est probablement la meilleure.

2.Combien de sujets comptez-vous démarrer ?

Si vous n'utilisez pas de pool de threads, je pense que vous devez soit limiter manuellement le nombre de threads, soit diviser les données en gros morceaux.Démarrer un nouveau fil de discussion pour chaque enregistrement laissera votre système en difficulté si le nombre d'enregistrements devient important.

L'ordinateur qui exécute le programme n'est probablement pas le goulot d'étranglement, donc :N'oubliez pas que le protocole HTTP possède un en-tête keep-alive, qui vous permet d'envoyer plusieurs requêtes GET sur les mêmes sockets, ce qui vous évite le handshake TCP/IP.Malheureusement, je ne sais pas comment l'utiliser dans les bibliothèques .net.(Cela devrait être possible.)

Il y aura probablement aussi du retard dans la réponse à vos demandes.Vous pouvez essayer de vous assurer que vous avez toujours un nombre donné de requêtes en attente adressées au serveur.

Obtenir le Effets parallèles.Regardez la BlockingCollection.Utilisez un thread pour lui fournir des lots d'enregistrements et 1 à n threads extrayant les enregistrements de la collection pour les mettre en service.Vous pouvez contrôler la vitesse à laquelle la collection est alimentée et le nombre de threads qui appellent les services Web.Rendez-le configurable via une ConfigSection, et rendez-le générique en alimentant la collection de délégués Action, et vous aurez un joli petit batcher que vous pourrez réutiliser à votre guise.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top