Autor Beitrag
Bergmann89
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: 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. :nixweiss:

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: So 17.10.10 15:34 
Hey,

ausblenden 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);
  //Prozesbar initialisieren wenn vorhanden
  if Assigned(ProgBar) then begin
    ProgBar.Min  := 0;
    ProgBar.Step := 1;
    Progbar.Max  := Count;
    ProgBar.Position := 0;
  end;
  //Indices initialisieren
  for i := 0 to 12 do
    IDs[i] := 1;
  Tip := GetString;

  //Schleife zur Berechnung der Kombinationen
  while not Finished do begin
    //tip := GetString;
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 39



BeitragVerfasst: 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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
  //Schleife zur Berechnung der Kombinationen
  while not Finished do begin
    //tip := GetString;
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 17.10.10 15:39 
user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
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:
user profile iconkalmi01 hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 111
Erhaltene Danke: 2

Win 7
Delphi 7 Prof, Turbo Delphi, VS 2008 Team System, VS 2010 Premium
BeitragVerfasst: 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:

ausblenden 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; // <- der Trick
    aProgressBar.Position := newPosition;
  end;
end;

Für diesen Beitrag haben gedankt: Bergmann89
kalmi01
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 39



BeitragVerfasst: So 17.10.10 15:47 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 17.10.10 15:50 
user profile iconlittleDave hat folgendes geschrieben Zum zitierten Posting springen:
Den Effekt zu deaktivieren habe ich noch nicht gefunden
Geht auch wie gesagt leider gar nicht.

user profile iconlittleDave hat folgendes geschrieben Zum zitierten Posting springen:
aber ein einfacher Work-Around schaut wie folgt aus:
Gute Idee. :zustimm:
Wichtig ist dann nur noch, dass PBS_SMOOTHREVERSE nicht gesetzt ist.

user profile iconkalmi01 hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: 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:
ausblenden volle Höhe 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:
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;

  //Prozesbar initialisieren wenn vorhanden
  if Assigned(fProgBar) then begin
    MaxX := GetCombinationCount(fCharSet)/100;
    Synchronize(InitProgressBar);
  end;
  //Indices initialisieren
  for i := 0 to 12 do
    IDs[i] := 1;
  Tip := GetString;

  //Schleife zur Berechnung der Kombinationen
  while not Finished do begin
    //tip := GetString;
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: So 17.10.10 16:13 
user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
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:
ausblenden volle Höhe 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:
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;

  //Prozesbar initialisieren wenn vorhanden
  if Assigned(fProgBar) then begin
    MaxX := GetCombinationCount(fCharSet)/100;
    Synchronize(InitProgressBar);
  end;
  //Indices initialisieren
  for i := 0 to 12 do
    IDs[i] := 1;
  Tip := GetString;

  //Schleife zur Berechnung der Kombinationen
  while not Finished do begin
    //tip := GetString;
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
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)
BeitragVerfasst: 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^^