2015-07-13 5 views
11

In einem C# -Projekt mache ich einige Aufrufe an eine Web-API, die Sache ist, dass ich sie innerhalb einer Schleife in einer Methode mache. Gewöhnlich gibt es nicht so viele, obwohl ich daran dachte, die Parallelität auszunutzen.Wo Parallelität beim Aufruf einer API zu verwenden ist

Was ich bisher versucht, ist

public void DeployView(int itemId, string itemCode, int environmentTypeId) 
{ 
    using (var client = new HttpClient()) 
    { 
     client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiUrl"]); 
     client.DefaultRequestHeaders.Accept.Clear(); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

     var agents = _agentRepository.GetAgentsByitemId(itemId); 

     var tasks = agents.Select(async a => 
      { 
       var viewPostRequest = new 
        { 
         AgentId = a.AgentId, 
         itemCode = itemCode, 
         EnvironmentId = environmentTypeId 
        }; 

       var response = await client.PostAsJsonAsync("api/postView", viewPostRequest); 
      }); 

     Task.WhenAll(tasks); 
    } 
} 

Aber frage mich, ob dies der richtige Weg ist, oder sollte ich versuchen, die ganze DeployView parallel (dh auch vor der Verwendung des Httpclient)

Jetzt, wo ich sehen sie geschrieben, ich glaube, ich kann nicht einfach die Variable Antwort entfernen und nur die await tun, ohne es zu irgendwelchen variable

Dank Einstellung

+2

Nun eigentlich, das ist eine gute Richtung, die Sie gehen. Aber du hast den wichtigsten Teil vergessen. Sie müssen die Ergebnisse z. * Warten Sie auf Task.WhenAll *, aber dann müssen Sie Ihrer DeployView-Funktion das Schlüsselwort 'async' hinzufügen. Sie sollten einen tieferen Einblick in das [async/await] (https://msdn.microsoft.com/en-us/library/hh191443.aspx) Paradigma erhalten. – ckruczek

+1

Also, was ist das Problem/die Ausnahme, die Sie konfrontiert werden? Ich stimme auch mit ckruczek überein, nichts falsch mit der Richtung, die Sie nehmen .. auch, wollen Sie die Antworten überhaupt bekommen? – ojf

+0

Ich möchte die Antworten bekommen, ja. Aber nicht sicher, wie man sie benutzt, wenn sie alle Ok sind – mitomed

Antwort

5

Was Sie Einführung ist Gleichzeitigkeit, nicht Parallelität.Mehr dazu here.

Ihre Richtung ist gut, wenn auch ein paar kleinere Änderungen, die ich machen würde:

Zunächst sollten Sie Ihre Methode als async Task markieren, wie Sie Task.WhenAll verwenden sind, die ein awaitable zurückgibt, die Sie asynchron benötigen warte auf. Als Nächstes können Sie den Vorgang einfach von PostAsJsonAsync zurückgeben, anstatt auf jeden Anruf innerhalb Ihrer Select zu warten. Dies wird ein wenig Aufwand sparen, da es nicht die Zustandsmaschine für den Asynchron-Aufruf generiert:

public async Task DeployViewAsync(int itemId, string itemCode, int environmentTypeId) 
{ 
    using (var client = new HttpClient()) 
    { 
     client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiUrl"]); 
     client.DefaultRequestHeaders.Accept.Clear(); 
     client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json")); 

     var agents = _agentRepository.GetAgentsByitemId(itemId); 
     var agentTasks = agents.Select(a => 
     { 
      var viewPostRequest = new 
      { 
       AgentId = a.AgentId, 
       itemCode = itemCode, 
       EnvironmentId = environmentTypeId 
      }; 

      return client.PostAsJsonAsync("api/postView", viewPostRequest); 
     }); 

     await Task.WhenAll(agentTasks); 
    } 
} 

HttpClient in der Lage, gleichzeitige Zugriffe (siehe @usr Link für weitere Informationen) zu machen, so I don‘ Ich sehe einen Grund, eine neue Instanz jedes Mal innerhalb Ihres Lambda zu erstellen. Beachten Sie, dass wenn Sie DeployViewAsync mehrere Male konsumieren, möchten Sie vielleicht Ihre HttpClient herum behalten, statt jedes Mal eine zuzuteilen, und sie entsorgen, sobald Sie ihre Dienste nicht mehr benötigen.

4

HttpClient appears to be usable for concurrent requests. Ich habe das selbst nicht verifiziert, das ist genau das, was ich aus der Suche entnehme. Daher müssen Sie für jede Aufgabe, die Sie starten, keinen neuen Client erstellen. Sie können tun, was Ihnen am bequemsten ist.

Im Allgemeinen versuche ich, so wenig (veränderlichen) Zustand wie möglich zu teilen. Ressourcenakquisitionen sollten generell in Richtung ihrer Nutzung verschoben werden. Ich denke, es ist besser, einen Helfer CreateHttpClient zu erstellen und einen neuen Client für jede Anfrage hier zu erstellen. Ziehen Sie in Betracht, den Select-Body zu einer neuen asynchronen Methode zu machen. Dann ist die HttpClient Verwendung vollständig von DeployView ausgeblendet.

Vergessen Sie nicht await die WhenAll Aufgabe und machen Sie die Methode async Task. (Wenn Sie nicht verstehen, warum das notwendig ist, dass Sie einige der Forschung über await zu tun habe.)

+0

Vielen Dank für Ihre aufschlussreiche Antwort, wie @YuvalItzchakov merkte Ich denke, es macht mehr Sinn für mich in diesem Fall den Client zu halten, da es für gleichzeitige Anfragen verwendbar ist, wie diese Methode mehrmals aufgerufen werden – mitomed

6

Normalerweise gibt es keine Notwendigkeit, die Anfragen parallelisieren - ein Thread Asynchron-Anfragen machen sollte genug sein (auch wenn Sie haben Hunderte von Anfragen). Betrachten Sie diesen Code:

var tasks = agents.Select(a => 
     { 
      var viewPostRequest = new 
       { 
        AgentId = a.AgentId, 
        itemCode = itemCode, 
        EnvironmentId = environmentTypeId 
       }; 

      return client.PostAsJsonAsync("api/postView", viewPostRequest); 
     }); 
    //now tasks is IEnumerable<Task<WebResponse>> 
    await Task.WhenAll(tasks); 
    //now all the responses are available 
    foreach(WebResponse response in tasks.Select(p=> p.Result)) 
    { 
     //do something with the response 
    } 

Sie können jedoch Parallelität bei der Verarbeitung der Antworten verwenden. Anstelle der oben ‚foreach‘ Schleife können Sie verwenden:

Parallel.Foreach(tasks.Select(p=> p.Result), response => ProcessResponse(response)); 

Aber TMO, ist dies die beste Nutzung von asynchronen und Parallelität:

var tasks = agents.Select(async a => 
     { 
      var viewPostRequest = new 
       { 
        AgentId = a.AgentId, 
        itemCode = itemCode, 
        EnvironmentId = environmentTypeId 
       }; 

      var response = await client.PostAsJsonAsync("api/postView", viewPostRequest); 
      ProcessResponse(response); 
     }); 
await Task.WhenAll(tasks); 

Es gibt einen großen Unterschied zwischen dem ersten und dem letzten Beispiele : In der ersten, haben Sie einen Thread Async-Anforderungen starten, wartet (nicht blockiert) für alle von ihnen zurückzukehren, und nur dann sie zu verarbeiten. Im zweiten Beispiel fügen Sie an jede Aufgabe eine Fortsetzung an. Auf diese Weise wird jede Antwort verarbeitet, sobald sie eintrifft. Unter der Annahme, dass der aktuelle TaskScheduler die parallele Ausführung von Tasks (Multithread) zulässt, bleibt keine Antwort im Leerlauf wie im ersten Beispiel.

* Edit - wenn Sie do entscheiden, es parallel zu tun, können Sie nur eine Instanz von HttpClient verwenden - es ist Thread sicher.

+0

Async IO macht das IO nicht schneller. Es ist unabhängig von der Geschwindigkeit dieses einzelnen IO. Er kann * nur * schneller parallelisieren. – usr

+0

Ich sagte nie Async macht das IO schneller. Das Posten einer einzelnen asynchronen Anfrage ist relativ schnell (es ist nicht nötig, auf eine Antwort zu warten), schnell genug, damit ein Thread tausende von ihnen in kürzester Zeit erstellen kann. Deshalb habe ich "Normalerweise" betont. –

Verwandte Themen