5

Ich suche ein Designmuster, das zu meinem Anwendungsdesign passen würde.Fortschrittsbalken und mehrere Threads, Entkopplung von GUI und Logik - welches Designmuster wäre das Beste?

Meine Anwendung verarbeitet große Datenmengen und erstellt einige Grafiken. Datenverarbeitung (Holen von Dateien, CPU-intensive Berechnungen) und Grafikoperationen (Zeichnen, Aktualisieren) werden in separaten Threads durchgeführt.

Grafik kann gescrollt werden - in diesem Fall müssen neue Datenteile verarbeitet werden. Da es mehrere Reihen in einem Graphen geben kann, können mehrere Threads erzeugt werden (zwei Threads pro Serie, einer für die Aktualisierung des Datensatzes und einer für die Aktualisierung des Graphen).

Ich möchte nicht mehrere Fortschrittsbalken erstellen. Stattdessen möchte ich einen einzigen Fortschrittsbalken haben, der über den globalen Fortschritt informiert. Im Moment kann ich an MVC und Observer/Observable denken, aber es ist ein wenig verschwommen :) Vielleicht könnte mir jemand in eine richtige Richtung zeigen, danke.

Antwort

1

Mehrere Fortschrittsbalken sind nicht so eine schlechte Idee, wohlgemerkt. Oder vielleicht ein komplexer Fortschrittsbalken, der mehrere laufende Threads zeigt (wie Download-Manager-Programme manchmal haben). Solange die Benutzeroberfläche intuitiv ist, werden Ihre Benutzer die zusätzlichen Daten zu schätzen wissen.

Wenn ich versuche, solche Designfragen zu beantworten, versuche ich zuerst, ähnliche oder analoge Probleme in anderen Anwendungen zu betrachten, und wie sie gelöst werden. Daher würde ich vorschlagen, dass Sie einige Nachforschungen anstellen, indem Sie andere Anwendungen in Betracht ziehen, die einen komplexen Fortschritt aufweisen (wie das Beispiel des Download-Managers) und versuchen, eine vorhandene Lösung an Ihre Anwendung anzupassen.

Entschuldigung, ich kann kein spezifischeres Design anbieten, das ist nur ein allgemeiner Hinweis. :)

1

Stick mit Observer/Observable für diese Art von Sache. Ein Objekt beobachtet die verschiedenen Serienverarbeitungs-Threads und meldet den Status durch Aktualisieren der Zusammenfassungsleiste.

6

Ich habe einmal den besten Teil einer Woche damit verbracht, einen glatten, nicht schluckigen Fortschrittsbalken über einen sehr komplexen Algorithmus zu erstellen.

Der Algorithmus hatte 6 verschiedene Schritte. Jeder Schritt hatte Timing-Eigenschaften, die stark von A) der zugrundeliegenden Daten, die verarbeitet wurden, nicht nur die "Menge" der Daten, sondern auch die "Art" der Daten und B) 2 der Schritte extrem skaliert mit zunehmender Anzahl der CPUs, 2 Schritte wurden in 2 Threads ausgeführt und 2 Schritte waren effektiv single-threaded.

Die Mischung der Daten hatte einen viel größeren Einfluss auf die Ausführungszeit jedes Schritts als die Anzahl der Kerne.

Die Lösung, die es schließlich geknackt hat, war wirklich ziemlich einfach. Ich machte 6 Funktionen, die den Datensatz analysierten und versuchten, die tatsächliche Laufzeit jedes Analyseschritts vorherzusagen. Die Heuristik in jeder Funktion analysierte sowohl die analysierten Datensätze als auch die Anzahl der CPUs. Basierend auf Laufzeitdaten von meinem eigenen 4-Kern-Rechner gab jede Funktion im Grunde die Anzahl der Millisekunden zurück, die sie voraussichtlich hatte, auf meinem Rechner.

f1 (..) + f2 (..) + f3 (..) + f4 (..) + f5 (..) + f6 (..) = Gesamtlaufzeit in Millisekunden

nun gegeben Mit dieser Information können Sie effektiv wissen, wie viel Prozent der gesamten Ausführungszeit für jeden Schritt benötigt wird. Wenn Sie nun sagen, dass Schritt1 40% der Ausführungszeit benötigt, müssen Sie im Grunde herausfinden, wie 40 1% -Ereignisse von diesem Algorithmus ausgegeben werden.Sprich die for-Schleife 100.000 Artikel verarbeitet, könnten Sie wahrscheinlich tun:

for (int i = 0; i < numItems; i++){ 
    if (i % (numItems/percentageOfTotalForThisStep) == 0) emitProgressEvent(); 
    .. do the actual processing .. 
} 

Dieser Algorithmus uns eine seidig glatte Fortschrittsbalken gab, die einwandfrei durchgeführt wird. Ihre Implementierungstechnologie kann verschiedene Formen der Skalierung und Funktionen in der Fortschrittsleiste haben, aber die grundlegende Art, über das Problem nachzudenken, ist die gleiche.

Und ja, es war nicht wirklich wichtig, dass die Heuristik-Referenznummern auf meinem Rechner ausgearbeitet wurden - das einzige wirkliche Problem ist, wenn Sie die Zahlen ändern wollen, wenn sie auf einem anderen Rechner laufen. Aber du kennst immer noch das Verhältnis (das ist die einzige wirklich wichtige Sache hier), also kannst du sehen, wie deine lokale Hardware anders läuft als die, die ich hatte.

Jetzt kann sich der durchschnittliche SO-Leser fragen, warum in aller Welt jemand eine Woche damit verbringen würde, einen reibungslosen Fortschritt zu machen. Das Feature wurde vom Chefverkäufer angefordert, und ich glaube, er hat es in Verkaufsmeetings benutzt, um Verträge zu bekommen. Money Talks;)

+1

Upvoted für die Coolness. – erikprice

2

In Situationen mit Threads oder asynchronen Prozessen/Aufgaben finde ich es hilfreich, einen abstrakten Typ oder ein Objekt im Hauptthread zu haben, das jeden Prozess repräsentiert (und idealerweise einkapselt). Für jeden Worker-Thread wird es vermutlich ein Objekt (nennen wir es Operation) im Haupt-Thread geben, um diesen Worker zu verwalten, und offensichtlich wird es irgendeine Art von listähnlicher Datenstruktur geben, um diese Operationen zu halten.

Wo vorgesehen, bietet jede Operation die Start/Stopp-Methoden für ihren Worker und in einigen Fällen - wie bei Ihnen - numerische Eigenschaften, die den Fortschritt und die erwartete Gesamtzeit oder Arbeit der jeweiligen Operation darstellen. Die Einheiten müssen nicht unbedingt zeitbasiert sein, wenn Sie wissen, dass Sie 6.230 Berechnungen durchführen werden, können Sie diese Eigenschaften einfach als Berechnungszähler betrachten. Darüber hinaus muss jede Aufgabe eine Möglichkeit haben, den laufenden Betrieb des aktuellen Fortschritts in jedem geeigneten Mechanismus zu aktualisieren (Rückrufe, Sperrungen, Ereignisverteilung oder welcher Mechanismus auch immer Ihre Programmiersprache/Threading-Framework bereitstellt).

Während also Ihre eigentliche Arbeit in separaten Threads ausgeführt wird, wird ein entsprechendes Vorgangsobjekt im "Haupt" -Thread ständig aktualisiert/über den Fortschritt seines Arbeiters informiert. Der Fortschrittsbalken kann sich entsprechend aktualisieren und die Summe der "erwarteten" Zeiten der Operationen auf ihre Summe und die Summe der "Fortschritts" -Zeiten der Operationen auf ihren aktuellen Fortschritt umrechnen, in welcher Weise auch immer dies für Ihr Fortschrittsbalkensystem sinnvoll erscheint.

Offensichtlich gibt es eine Menge anderer Überlegungen/Arbeit, die getan werden muss, um dies tatsächlich zu implementieren, aber ich hoffe, dass dies Ihnen den Kern davon gibt.

Verwandte Themen