2017-07-25 14 views
2

Ich habe eine Konsolenanwendung, die eine Datenbank abfragt und dann einige Datensätze in einer Schleife an eine REST-API sendet (die API unterstützt keine Stapelbuchung, daher muss ich durchgehen jeder Datensatz und Posten einzeln, wenn es relevant ist). Der Zugriff auf die Datenbank ist schnell und problemlos, und auch die API-Post-Schleife entspricht dem Timer, den ich eingerichtet habe. Allerdings benötigt die App selbst nach dem Ende der Arbeit viel Zeit zum Beenden.Console App dauert nach Abschluss der Arbeit sehr lange

Dies begann, nachdem ich Parallel.Foreach eingeführt, um die Buchung zu beschleunigen. Vor der Verwendung einer nicht-parallelen Schleife dauerte die Veröffentlichung von 1000 Datensätzen durchschnittlich ~ 10 Minuten, aber die App würde zurückkehren und sofort wieder beenden, wenn sie fertig war (wie erwartet). Mit der parallelen Schleife ist diese nach dem Stopwatch Timer, den ich verwende, auf einen Durchschnitt von ~ 44 Sekunden reduziert, jedoch die App nicht beendet, bis etwa 2 Minuten vergangen sind - ~ 1min15sek nachdem alle Arbeit abgeschlossen ist.

Die App macht nichts "extra". Es gibt main ein, main ruft eine Methode auf, um einige Datensätze aus einer Datenbank abzurufen (1-2 Sekunden), leitet 1000 dieser Datensätze an eine andere Methode weiter, die sie durchläuft und jede an die API sendet und dann beendet. Außer, es wird in diesem Fall aus irgendeinem Grund nicht sofort beendet.

Ich setze einen stopwatch Timer in main unmittelbar vor dem Aufruf der Buchungsmethode und protokolliere die Zeit unmittelbar nach der Methode zurück, und der Timer richtet sich nach dem Timer innerhalb der Methode, durchschnittlich ~ 46 Sekunden. Die Verzögerung tritt also auf, nachdem die Post-Methode zurückgegeben wurde, aber bevor die main-Funktion beendet wird, aber es ist nichts definiert, um dies zu tun. Debugging zeigte nichts Außergewöhnliches. Handelt es sich um ein Problem mit der Aufhebung der Zuweisung, das sich auf alle Objekte bezieht, die von der parallelen Schleife erzeugt werden, die "herumhängen"?

Das passiert unabhängig davon, ob ich mit einem Debugger verbunden bin oder die Binärdatei direkt ausführe, wenn sie für die Veröffentlichung erstellt wird (also kein Problem mit der Entfernungsverzögerung). Ich habe mir andere SO-Fragen wie diese angeschaut, aber ihre Ansätze haben keinen Unterschied gemacht. Jede Eingabe würde geschätzt werden.

-Code der Buchungsfunktion:

public ProcessingState PostClockingRecordBatchParallel(List<ClockingEvent> batch, int tokenExpiryTolerance) 
{ 
    log.Info($"Attempting to post batch of {batch.Count.ToString()} clocking records to API with an auth token expiry tolerance of {tokenExpiryTolerance} seconds"); 
    try 
    { 
     ProcessingState state = new ProcessingState() { PendingRecords = batch }; 
     List<ClockingEvent> successfulRecords = new List<ClockingEvent>(); 
     Stopwatch timer = new Stopwatch(); 

     ServicePointManager.UseNagleAlgorithm = false; //Performance optimization related to RestSharp lib 
     authToken = Authenticate(); 

     timer.Start(); 
     Parallel.ForEach(state.PendingRecords, pr => 
     { 
      successfulRecords.Add(PostClockingRecord(pr, tokenExpiryTolerance)); 
     }); 
     //Prior non-parallel version 
     //state.PendingRecords.ForEach(pr => 
     //{ 
     // successfulRecords.Add(PostClockingRecord(pr, tokenExpiryTolerance)); 
     //}); 


     state.PendingRecords  = state.PendingRecords.Except(successfulRecords).ToList(); 
     state.LastSuccessfulRecord = successfulRecords.OrderBy(r => r.EventID).Last().EventID; 

     log.Info($"PostClockingRecordBatchParallel - Time elapsed: {new TimeSpan(timer.ElapsedTicks).ToString()}"); 
     return state; 
    } 
    catch (Exception ex) 
    { 
      log.Fatal($"Failed to post records to API (exception encountered: {ex})."); 
     throw; 
    } 
} 

Antwort

1

Ja, es wäre, den Speicher frei. Ihr Thread belegt Speicher und Sie können dies durch Verwendung von ParallelOptions.MaxDegreeOfParallelism Property begrenzen, wodurch die Abfrage natürlich verlangsamt wird und Sie die Speicherfreigabe verwalten müssen, wenn Sie die Zeit zum Beenden der Anwendung reduzieren möchten.

Sie können dispose of your tasks, wenn Skalierbarkeit ein Problem ist und Sie zu viel Speicher verbrauchen, oder Ressourcen aufräumen möchten, wie Sie gehen. Als Parallel class erweitert die Task-Klasse.

Obwohl das Aufrufen des Garbage Collectors möglicherweise ein narrensicherer Entwurf für Sie ist.

How can I free-up memory used by a Parallel.Task?

Um die Garbage Collection am Ende des Laufes reduzieren, können Sie Ihre eigene Garbage Collection umsetzen können, wie in this answer

Action allCollect =() => 
     { 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 
     }; 

gezeigt, wo Sie in regelmäßigen Abständen manuell für die Garbage Collection aufrufen können.

Ebenfalls hilfreich:
Possible memoryleak in ConcurrentBag?

This answer gibt Beispiele dafür, wie MaxDegreeOfParallelism

ParallelOptions.MaximumDegreeOfParallelism = 1: use one full CPU (which will be a percentage of your OS CPU) 

verwenden Verwalten dies wichtig ist, wenn Sie Ihre Anwendung skalieren, um Speicherlecks und OutOfMemoryException zu vermeiden.

+1

Danke, genau das, was ich gesucht habe! – Hangman

Verwandte Themen