Autor |
Beitrag |
Bergmann89
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 15:15
Hey,
ich schreib zur Zeit an einem Programm, das ziemlich CPU intensive Berechnungen durchführt. Zur Anzeige des Fortschritts bensutz ich ne ProgressBar. Die kommt aber irgendwie nich ganz hinterher. Wenn ich die Berechnungen starte, fängt sie erst nach ca. einer Sekunde an sich zu bewegen (und das auch ziemlich ruckelnd). Dann sagt mir mein Programm, das die Berechnungen beendet sind, aber die Progressbar bewegt sich immer noch (jetzt wieder flüssig, wie es normalerweiße ist). ProgressMessages hat leider auch nicht geholfen. Weiß jmd wie ich das beheben könnte? Oder sollte ich die Berechnungen lieber gleich in einern extra Thread auslagern?
MfG & Thx Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: So 17.10.10 15:24
Da wär mal interessant, wie die Berechnungen aussehen, und wann und wie und wie oft du die Progressbar aktualisierst.
Berechnungen in einen Thread und ab und zu eine Message an das Fenster "ich habe jetzt 42% fertig", damit sich die Progressbar verändert, dürfte aber das beste für sowas sein. 
_________________ We are, we were and will not be.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 17.10.10 15:31
Also ein Thread ist sicher ohnehin die beste Lösung wie schon gesagt, aber ob das an dem Phänomen etwas ändert, bin ich noch nicht so sicher.
Kann es sein, dass du Windows Vista oder 7 einsetzt? Dort versucht Windows die Fortschrittsanzeige zu glätten, was in der Regel auch ganz gut funktioniert. Wenn aber die Fortschrittsanzeige z.B. zuerst relativ langsam voranschreitet und dann plötzlich schnell fertig wird, hängt die Anzeige etwas hinterher. Einfach um nicht so plötzlich die Fortschrittsanzeige springen zu lassen.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 15:34
Hey,
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| if Assigned(List) then List.Clear; Finished := False; Count := GetCombinationCount(CharSet); if Assigned(ProgBar) then begin ProgBar.Min := 0; ProgBar.Step := 1; Progbar.Max := Count; ProgBar.Position := 0; end; for i := 0 to 12 do IDs[i] := 1; Tip := GetString;
while not Finished do begin if Assigned(List) then List.Add(Tip);
if Assigned(ProgBar) then begin ProgBar.StepIt; end;
Finished := incID(0); end; |
so sieht das ganze aus. Aber egal ob Count 1 ist (Minimum) oder 1.594.323 (Maximum) es hängt immer hinterher. Ich denke auch, das es mit dem Thead hier nicht getan ist. (werd mich jetzt aber trotzdem dran machen das auf Thead umzubauen). Und wie du richtig vermutest hast, nutz ich Win7. Kann man das nich iwie abstellen?!
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
kalmi01
      
Beiträge: 39
|
Verfasst: So 17.10.10 15:34
Application.ProcessMessages;
Sleep(0); // oder Sleep(1) wenn andere Programme auch noch zum Zuge kommen sollen
gehört eigentlich in jeden Schleifendurchlauf.
So reagiert das Prg. deutlich flüssiger auf Einflüsse von Aussen oder auch auf Refresh.
Z.B. so: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| while not Finished do begin if Assigned(List) then List.Add(Tip);
if Assigned(ProgBar) then begin ProgBar.StepIt;
[b]Application.ProcessMessages; Sleep(0);[/b] end;
Finished := incID(0); end; |
Zuletzt bearbeitet von kalmi01 am So 17.10.10 15:44, insgesamt 1-mal bearbeitet
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 17.10.10 15:39
Bergmann89 hat folgendes geschrieben : | Kann man das nich iwie abstellen?! |
Nein, das ist nicht möglich, wenn du die Themes benutzt. Die einzige Möglichkeit ist das Theme mit SetWindowTheme für die ProgressBar selektiv abzuschalten, aber dann sieht die entsprechend Sch*** wie bei Win9x aus.
// EDIT:
kalmi01 hat folgendes geschrieben : | gehört eigentlich in jeden Schleifendurchlauf.
So reagiert das Prg. deutlich flüssiger auf Einflüsse von Aussen oder auch auf Refresh. |
Dafür dauert der Vorgang an sich extrem viel länger...
// EDIT2:
Und das Sleep ist überflüssig. Ich habe es gerade ausprobiert, Sleep(0) kehrt sofort zurück, bringt also gar nichts.
Zuletzt bearbeitet von jaenicke am So 17.10.10 15:45, insgesamt 1-mal bearbeitet
|
|
littleDave
      
Beiträge: 111
Erhaltene Danke: 2
Win 7
Delphi 7 Prof, Turbo Delphi, VS 2008 Team System, VS 2010 Premium
|
Verfasst: So 17.10.10 15:44
Benutzt du Vista oder Win7 mit Aero? Wenn ja, dann liegt das Hinterherhinken an Aero. Per Design ist es so, dass die ProgressBar "smooth" gezeichnet wird. D.h. beim setzen von Position wird nicht sofort die entsprechende Position gezeichnet, sondern die ProgressBar wandert langsam zu diesem Punkt. Den Effekt zu deaktivieren habe ich noch nicht gefunden (jedenfalls nicht ohne die Themes zu deaktivieren), aber ein einfacher Work-Around schaut wie folgt aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure SetProgressBarPosition(aProgressBar: TProgressBar; newPosition: integer); begin if aProgressBar.Position <> newPosition then begin aProgressBar.Position := aProgressBar.Max; aProgressBar.Position := newPosition; end; end; |
Für diesen Beitrag haben gedankt: Bergmann89
|
|
kalmi01
      
Beiträge: 39
|
Verfasst: So 17.10.10 15:47
jaenicke hat folgendes geschrieben : |
Sleep(0) und Sleep(1) gibt (da das Minimum ohnehin 10-15 Millisekunden oder so sind, je nach System). |
Sleep(0) lässt Dein Prg. auf eigene Messages reagieren
Sleep(1) lässt auch andere Prozesse zu Wort kommen
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 17.10.10 15:50
littleDave hat folgendes geschrieben : | Den Effekt zu deaktivieren habe ich noch nicht gefunden |
Geht auch wie gesagt leider gar nicht.
littleDave hat folgendes geschrieben : | aber ein einfacher Work-Around schaut wie folgt aus: |
Gute Idee.
Wichtig ist dann nur noch, dass PBS_SMOOTHREVERSE nicht gesetzt ist.
kalmi01 hat folgendes geschrieben : | Sleep(0) lässt Dein Prg. auf eigene Messages reagieren
Sleep(1) lässt auch andere Prozesse zu Wort kommen |
Durch Sleep wird einfach nur der Thread schlafen gelegt. Da kann keine Message-Behandlung stattfinden.
Dass andere Prozesse / Threads über Zeitscheibe dran kommen, stimmt schon, aber dennoch ist es zumindest ein Tick, der verloren geht, wenn gerade keine CPU im Leerlauf ist.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 16:13
Hey,
danke Dave, funzt 1a^^ Leider is das Programm durch den Thread jetzt nur noch halb so schnell, obwohl ich das Update der ProgressBar erst aufrufe, wenn mind. ein ganzes Prozent dazu gekommen ist. Das heißt ich hab max. 100 Synchronize-Aufrufe. Da es aber bei dem Programm auf die Laufzeit ankommt kann ich mit der Hälfte langsamer nicht leben. Gibt noch ne Möglichkeit das schnell zu machen, oder sollte ich vlt. doch bei der Variante ohne Thread bleiben?!
so sieht das Ganze jetzt aus:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
| procedure TCalculateTipsThread.InitProgressBar; begin fProgBar.Min := 0; fProgBar.Max := 100; fProgBar.Position := 0; fProgBar.Step := 1; end;
procedure TCalculateTipsThread.StepProgressBar; var OldPos: Integer; begin OldPos := fprogbar.Position; fProgBar.Position := fProgBar.Max; fProgbar.Position := OldPos + 1; end;
procedure TCalculateTipsThread.Execute; x := 0; StartTime := GetTickCount;
if Assigned(fTipList) then fTipList.Clear; Finished := False;
if Assigned(fProgBar) then begin MaxX := GetCombinationCount(fCharSet)/100; Synchronize(InitProgressBar); end; for i := 0 to 12 do IDs[i] := 1; Tip := GetString;
while not Finished do begin if Assigned(fTipList) then fTipList.Add(Tip);
if Assigned(fProgBar) then begin x := x + 1; if x >= MaxX then begin x := x - MaxX; Synchronize(StepProgressBar); end; end;
Finished := incID(0); end;
fCalcTime := GetTickCount - StartTime; end; |
MfG Bergmann
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
Zuletzt bearbeitet von Bergmann89 am So 17.10.10 16:15, insgesamt 1-mal bearbeitet
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 16:13
Bergmann89 hat folgendes geschrieben : | Hey,
danke Dave, funzt 1a^^ Leider is das Programm durch den Thread jetzt nur noch halb so schnell, obwohl ich das Update der ProgressBar erst aufrufe, wenn mind. ein ganzes Prozent dazu gekommen ist. Das heißt ich hab max. 100 Synchronize-Aufrufe. Da es aber bei dem Programm auf die Laufzeit ankommt kann ich mit der Hälfte langsamer nicht leben. Gibts noch ne Möglichkeit das schnell zu machen, oder sollte ich vlt. doch bei der Variante ohne Thread bleiben?!
so sieht das Ganze jetzt aus:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
| procedure TCalculateTipsThread.InitProgressBar; begin fProgBar.Min := 0; fProgBar.Max := 100; fProgBar.Position := 0; fProgBar.Step := 1; end;
procedure TCalculateTipsThread.StepProgressBar; var OldPos: Integer; begin OldPos := fprogbar.Position; fProgBar.Position := fProgBar.Max; fProgbar.Position := OldPos + 1; end;
procedure TCalculateTipsThread.Execute; x := 0; StartTime := GetTickCount;
if Assigned(fTipList) then fTipList.Clear; Finished := False;
if Assigned(fProgBar) then begin MaxX := GetCombinationCount(fCharSet)/100; Synchronize(InitProgressBar); end; for i := 0 to 12 do IDs[i] := 1; Tip := GetString;
while not Finished do begin if Assigned(fTipList) then fTipList.Add(Tip);
if Assigned(fProgBar) then begin x := x + 1; if x >= MaxX then begin x := x - MaxX; Synchronize(StepProgressBar); end; end;
Finished := incID(0); end;
fCalcTime := GetTickCount - StartTime; end; |
MfG Bergmann |
€: Shit, ich hab auf Zitieren un nich auf Editieren geklickt ^^
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 17.10.10 16:18
Ganz einfach: Synchronisiere nicht, das ist immer teuer. Und das gilt auch für das Aktualisieren der Oberfläche insgesamt, wenn du Repaint oder Application.ProcessMessages ohne Thread benutzt.
Die Lösung ist viel einfacher: schicke einfach aus dem Thread eine Message mit der Prozentangabe z.B. in LParam an dein Fenster. Die landet dann im Hauptthread und du kannst direkt die Fortschrittsanzeige aktualisieren. Gleichzeitig läuft der Thread aber einfach weiter.
// EDIT:
Ach ja, nebenbei:
Das Übergeben der ganzen ProgressBar an eine andere Klasse oder hier sogar den Thread ist eher schlechter Programmierstil (Stichwort: objektorientierte Programmierung und Kapselung). Sage besser nur im Hauptfenster per Event Bescheid und lasse sowas da machen (so wie du es hier dann mit der Message machst).
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 16:23
Okay, das hab ich noch nie gemacht, sollte aber nich so schwer sein. Die Messages sind ja Cardinal-Werte. Gibts da nen bestimmten Bereich, in dem der User seine Messages selbst definieren kann? Nich das ich dann den gleichen Wert für meine Message nehme, wie eine System-Message.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 17.10.10 16:24
Innerhalb der Anwendung:
WM_USER + X, aber <= $7FFF
Zwischen Anwendungen:
WM_APP + X, aber <= $BFFF
Siehe:
msdn.microsoft.com/e...ms644931(VS.85).aspx
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 17.10.10 16:29
Ah, super! Ab jetzt sollte ich allein klar kommen. Dankeschön!
€: Wow, mit Thread komm ich auf 0,3sec anstatt 2sec. Liegt aber auch daran, das ich die ProgressBar nicht so oft aktualisiere.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
|