Frage

Betrachten Sie dieses Problem:Ich habe ein Programm, das (sagen wir) 100 Datensätze aus einer Datenbank abrufen und dann für jeden einzelnen aktualisierte Informationen von einem Webdienst erhalten soll.Es gibt zwei Möglichkeiten, in diesem Szenario Parallelität einzuführen:

  1. Ich starte jede Anfrage an den Webdienst in einem neuen Thread.Die Anzahl der gleichzeitigen Threads wird durch einen externen Parameter gesteuert (oder irgendwie dynamisch angepasst).

  2. Ich erstelle kleinere Stapel (z. B. mit jeweils 10 Datensätzen) und starte jeden Stapel in einem separaten Thread (in unserem Beispiel also 10 Threads).

Welcher Ansatz ist besser und warum glauben Sie das?

War es hilfreich?

Lösung

Option 3 ist die beste:

Verwenden Sie Async IO.

Sofern Ihre Anfrageverarbeitung nicht komplex und umfangreich ist, verbringt Ihr Programm 99 % seiner Zeit damit, auf HTTP-Anfragen zu warten.

Genau dafür ist Async IO konzipiert: Lassen Sie den Windows-Netzwerkstapel (oder das .net-Framework oder was auch immer) sich um das ganze Warten kümmern und verwenden Sie einfach einen einzigen Thread, um die Ergebnisse zu versenden und „abzuholen“.

Leider macht es das .NET-Framework zu einer echten Nervensäge.Es ist einfacher, wenn Sie nur Raw-Sockets oder die Win32-API verwenden.Hier ist ein (getestetes!) Beispiel, das trotzdem C#3 verwendet:

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

BEARBEITEN:

Im Fall von .NET wird der „Completion Callback“ tatsächlich in einem ThreadPool-Thread und nicht in Ihrem Hauptthread ausgelöst, sodass Sie weiterhin alle gemeinsam genutzten Ressourcen sperren müssen, aber es erspart Ihnen trotzdem die Mühe, Threads zu verwalten.

Andere Tipps

Zwei Dinge, die es zu beachten gilt.

1.Wie lange dauert die Bearbeitung eines Datensatzes?

Wenn die Datensatzverarbeitung sehr schnell erfolgt, kann der Mehraufwand für die Übergabe von Datensätzen an Threads zu einem Engpass werden.In diesem Fall möchten Sie die Datensätze bündeln, damit Sie sie nicht so oft weitergeben müssen.

Wenn die Datensatzverarbeitung relativ lange dauert, ist der Unterschied vernachlässigbar, daher ist der einfachere Ansatz (1 Datensatz pro Thread) wahrscheinlich der beste.

2.Wie viele Threads planen Sie zu starten?

Wenn Sie keinen Threadpool verwenden, müssen Sie meines Erachtens entweder die Anzahl der Threads manuell begrenzen oder die Daten in große Blöcke aufteilen.Das Starten eines neuen Threads für jeden Datensatz führt dazu, dass Ihr System überlastet wird, wenn die Anzahl der Datensätze zu groß wird.

Der Computer, auf dem das Programm ausgeführt wird, ist wahrscheinlich nicht der Flaschenhals, also:Denken Sie daran, dass das HTTP-Protokoll über einen Keep-Alive-Header verfügt, mit dem Sie mehrere GET-Anfragen an dieselben Sockets senden können, was Ihnen den TCP/IP-Handshake erspart.Leider weiß ich nicht, wie ich das in den .net-Bibliotheken verwenden soll.(Sollte möglich sein.)

Es wird wahrscheinlich auch zu Verzögerungen bei der Beantwortung Ihrer Anfragen kommen.Sie könnten versuchen, sicherzustellen, dass Sie immer eine bestimmte Anzahl ausstehender Anfragen an den Server haben.

Bekommen das Paralleler FX.Schauen Sie sich die BlockingCollection an.Verwenden Sie einen Thread, um ihm Stapel von Datensätzen zuzuführen, und 1 bis n Threads, die Datensätze aus der Sammlung abrufen, um sie zu bedienen.Sie können die Rate, mit der die Sammlung zugeführt wird, und die Anzahl der Threads steuern, die Webdienste aufrufen.Machen Sie es über eine ConfigSection konfigurierbar und machen Sie es generisch, indem Sie die Action-Delegierten der Sammlung füttern, und Sie haben einen netten kleinen Batcher, den Sie nach Herzenslust wiederverwenden können.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top