PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Heureka! (Schleifen in Delphi-Programmen abbrechen)


Vedek Bareil
2002-11-28, 02:31:13
Hallo!

Möglicherweise gibt es unter euch ja jemanden, der bei mit Delphi erstellten Programmen ein Problem hat, wie ich es 4 Jahre lang gehabt habe:
wenn das Ablaufen einer Schleife sehr lange dauert (z.B. mehrere Sekunden oder noch länger), so ist es nicht möglich, die Schleife abzubrechen, und solange die Schleife weiterläuft reagiert das Programm nicht, sondern verhält sich wie abgestürzt.

Nun habe ich durch Zufall die Lösung des Problems gefunden, und will euch diese nicht vorenthalten :)
Und zwar besteht die Lösung in dem Kommando Application.ProcessMessages, dieses schreibt man in die Schleife hinein, und dadurch wird bewerkstelligt, daß bei jedem Schleifendurchlauf der Eingang externer Meldungen (z.B. die, daß die Schleife abgebrochen werden soll) gecheckt wird, bevor die Schleife fortgesetzt wird. Dadurch wird ein Abbrechen der Schleife ermöglicht.

Um das mal an einem Beispiel zu verdeutlichen, betrachten wir eine Endlosschleife:

var isRunning: boolean;
...
isRunning:=True;
while isRunning do begin
isRunning:=True;
end;

wenn diese Schleife gestartet wird, kann das Programm nur noch von der Delphi-Entwicklungsumgebung oder per Strg+Alt+Entf beendet werden. Die folgende Modifikation:

var isRunning: boolean;
...
isRunning:=True;
while isRunning do begin
isRunning:=True;
Application.ProcessMessages;
end;
...
procedure TForm1.Schleife_AbbrechenClick(Sender: TObject);
begin
isRunning:=False;
end;

ermöglicht es, die Endlosschleife durch Anklicken der Schaltfläche Schleife_Abbrechen zu beenden.

Allerdings muß man beachten, daß das Programm u.U. extrem ausgebremst wird, wenn Application.ProcessMessages bei jedem Schleifendurchlauf aufgerufen wird. Soll eine Schleife z.B. mehrere Millionen mal pro Sekunde durchlaufen werden, so sollte das Kommando z.B. nur bei jedem 1000. Durchlauf aufgerufen werden, was sich etwa folgendermaßen realisieren ließe:

var isRunning: boolean; i: integer;
...
i:=0;
while isRunning do begin
...
i:=i+1;
if i=1000 then begin
i:=0;
Application.ProcessMessages;
end;
end;

[EF]peppa
2002-11-28, 04:07:43
das erste ist korrekt. eleganter ist (wenn notwaendig bzw sinnvoll) dass du die procedur/function mit der schleife in einem eigenem thread startest.

aaaaaber beim 2. setzt man einfach sleep(1) und die sache hat sich erledigt. :D
etwa so:
repeat
//enter code here
Application.Processmessages;
sleep(1);
until 1=2


wenn du eine schleife verlassen willst:
break;

mfg peppa

Marcel
2002-11-28, 12:39:46
Originally posted by [EF]peppa
wenn du eine schleife verlassen willst:
break;


mag sein, dass das nur eine persönliche, subjektive Aversion gegen das break ist, aber in meinen Augen ist break nicht anderes als goto, Version 2.0. Übersichtlichkeitsfördernd ist das nicht gerade.

Gruß,
Marcel

[EF]peppa
2002-11-28, 17:40:54
Originally posted by Marcel


mag sein, dass das nur eine persönliche, subjektive Aversion gegen das break ist, aber in meinen Augen ist break nicht anderes als goto, Version 2.0. Übersichtlichkeitsfördernd ist das nicht gerade.

Gruß,
Marcel

da hasst recht, glaub ich ich versuche break immer zu vermeiden und habs erst einmal verwendet. besonders goto sollte man ja vermeiden.

mfg peppa

Darkstar
2002-11-28, 18:57:04
Originally posted by Marcel
mag sein, dass das nur eine persönliche, subjektive Aversion gegen das break ist, aber in meinen Augen ist break nicht anderes als goto, Version 2.0. Übersichtlichkeitsfördernd ist das nicht gerade.Ich bin da anderer Meinung. Schließlich hängt es stark davon ab, wie man den Quellcode strukturiert.

Wie gehst Du eigentlich vor, wenn Du eine Schleife/Prozedur schnell verlassen willst?

Marcel
2002-11-28, 19:08:37
Originally posted by Darkstar
Ich bin da anderer Meinung. Schließlich hängt es stark davon ab, wie man den Quellcode strukturiert.

Wie gehst Du eigentlich vor, wenn Du eine Schleife/Prozedur schnell verlassen willst?

Mit sauber benannten Flags, wenn es denn derartiges sein muss. Auf jeden Fall so, dass man allein aus dem Schleifenkopf (bzw. bei fußgesteuerten Schleifen aus dem Schleifenfuß) erkennen kann, unter welchen Bedingungen eine Schleife verlassen werden kann. Und das geht beim break eben nicht, da muss ich mir die Zeile mit dem break auch noch anschauen. Sprich, diese Zeile erst noch aufsuchen.

Xmas
2002-11-28, 19:24:26
Originally posted by Marcel
Mit sauber benannten Flags, wenn es denn derartiges sein muss. Auf jeden Fall so, dass man allein aus dem Schleifenkopf (bzw. bei fußgesteuerten Schleifen aus dem Schleifenfuß) erkennen kann, unter welchen Bedingungen eine Schleife verlassen werden kann. Und das geht beim break eben nicht, da muss ich mir die Zeile mit dem break auch noch anschauen. Sprich, diese Zeile erst noch aufsuchen.
Finde ich aber recht umständlich, weil du dann alles nach den false-Setzen des Flags in der Schleife in eine if-Anweisung einklammern musst. Gerade bei mehreren break-Möglichkeiten und Verschachtelungen können das viele ifs werden.

Xmas
2002-11-28, 19:26:39
Originally posted by [EF]peppa
das erste ist korrekt. eleganter ist (wenn notwaendig bzw sinnvoll) dass du die procedur/function mit der schleife in einem eigenem thread startest.

aaaaaber beim 2. setzt man einfach sleep(1) und die sache hat sich erledigt. :D

Ähh, sleep verlangsamt aber doch die Ausführung der Schleife, und das ist ja nicht gewünscht.

Marcel
2002-11-28, 19:31:26
Originally posted by Xmas

Finde ich aber recht umständlich, weil du dann alles nach den false-Setzen des Flags in der Schleife in eine if-Anweisung einklammern musst. Gerade bei mehreren break-Möglichkeiten und Verschachtelungen können das viele ifs werden.

Nun gut, da kann man jetzt 'ne Glaubensfrage draus machen.
Ich denke, dass break vielleicht etliche Male einfacher und schneller zu implementieren ist, die Flag-Methode aber später besser zu warten ist. Und als Programmierer ist man ja nunmal ständig im Trade-Off zwischen schneller Implementierung und guter Wartbarkeit gefangen.
Ich persönlich habe break beim ersten Kennenlernen spontan nicht gemocht, so ohne wirklichen Grund, und kam danach auch fast vollständig ohne aus.

Demirug
2002-11-28, 19:38:07
Originally posted by Marcel


Nun gut, da kann man jetzt 'ne Glaubensfrage draus machen.
Ich denke, dass break vielleicht etliche Male einfacher und schneller zu implementieren ist, die Flag-Methode aber später besser zu warten ist. Und als Programmierer ist man ja nunmal ständig im Trade-Off zwischen schneller Implementierung und guter Wartbarkeit gefangen.
Ich persönlich habe break beim ersten Kennenlernen spontan nicht gemocht, so ohne wirklichen Grund, und kam danach auch fast vollständig ohne aus.

Aus meiner Erfahrung (OK C++ und C#) ist ein break (sowie ein return) in der Wartung wesentlich einfacher zu handhaben weil sich damit die struktur flach halten läst. Es gibt nichts schlimeres als Code-Cascaden.

[EF]peppa
2002-11-28, 19:41:16
Originally posted by Xmas

Ähh, sleep verlangsamt aber doch die Ausführung der Schleife, und das ist ja nicht gewünscht.

1. was verlangsamt mehr?

sleep(1)

oder
var isRunning: boolean; i: integer;
...
i:=0;
while isRunning do begin
...
i:=i+1;
if i=1000 then begin
i:=0;
Application.ProcessMessages;
end;
end;

2. meinte ich dass die elegantere loesung sowieso ein eigener thread ist (siehe 2. post). den kann ich ein eine prioritaet geben.


mfg peppa

Xmas
2002-11-28, 19:48:21
Originally posted by [EF]peppa
1. was verlangsamt mehr?

sleep(1)

oder
var isRunning: boolean; i: integer;
...
i:=0;
while isRunning do begin
...
i:=i+1;
if i=1000 then begin
i:=0;
Application.ProcessMessages;
end;
end;

Äh, was soll das sleep denn bringen, außer dass es die Schleife abbremst?
Die zweite Variante führt doch nur dazu dass ProcessMessages nur jeden 1000. Schleifendurchlauf aufgerufen wird, das Beschleunigt die Schleifenausführung sogar (gegenüber dem in-jedem-Durchlauf-aufrufen natürlich).



2. meinte ich dass die elegantere loesung sowieso ein eigener thread ist (siehe 2. post). den kann ich ein eine prioritaet geben.
Jop, das ist die beste Lösung.

[EF]peppa
2002-11-28, 20:03:37
Originally posted by Xmas

Äh, was soll das sleep denn bringen, außer dass es die Schleife abbremst?
Die zweite Variante führt doch nur dazu dass ProcessMessages nur jeden 1000. Schleifendurchlauf aufgerufen wird, das Beschleunigt die Schleifenausführung sogar (gegenüber dem in-jedem-Durchlauf-aufrufen natürlich).[/SIZE]

uuups. hab mich verschaut. :sulkoff:
da hast du natuerlich wahr.

es kommt auch immer darauf an fuer wen die anwendung sein soll.
bzw wieviel aufwand die anwendung denn wert ist.

mfg peppa

Vedek Bareil
2002-11-28, 20:14:29
Originally posted by [EF]peppa
das erste ist korrekt. eleganter ist (wenn notwaendig bzw sinnvoll) dass du die procedur/function mit der schleife in einem eigenem thread startest. du meinst in einer eigenen Unit, also einem eigenen TForm-Objekt?


aaaaaber beim 2. setzt man einfach sleep(1) und die sache hat sich erledigt. :D
etwa so:
repeat
//enter code here
Application.Processmessages;
sleep(1);
until 1=2
daß das nur das Gegenteil von dem bewirken würde, was ich erreichen will, hat ja Xmas schon für mich erklärt ;)

[EF]peppa
2002-11-28, 20:33:25
Originally posted by Vedek Bareil
du meinst in einer eigenen Unit, also einem eigenen TForm-Objekt?

daß das nur das Gegenteil von dem bewirken würde, was ich erreichen will, hat ja Xmas schon für mich erklärt ;)

du machst eine eigene class.
diese hat einen constructor.

type
<classname> = class(TThread)
...
end;

koennte ungefaehr so aussehen.
TDLLrun = class(TThread)
private
DLLinit :TDLLinit;
DLLhandle :THandle;
DLLrun :TDLL_run;
DLLprogversion :TDLL_progversion;
DLLprogtype :TDLL_progtype;
DLLversion :TDLL_dllversion;
DLLgettree :TDLL_dllgettree;
MDIChild :TDLL_MDIChild;
DLLname :String;
procedure connectdll;
procedure getfunctions;
//andere proceduren
//andere proceduren
protected
workstream :Tstringstream;
streaminuse :boolean;
ende :boolean;
frequency :integer;
workposition :int64;
procedure Execute; override;
public
OK :String;
dllinfo :string;
constructor Create(dll :string);
function getworkstream :Tstringstream;
procedure beenden;
end;
(hab ich aus einem programm dass eine dll waehrend der laufzeit laden kann und deren form anzeigt. auf der man dann auch arbeiten kann)


mfg peppa

Vedek Bareil
2002-11-29, 00:58:29
Danke peppa, das werde ich bei Gelegenheit mal ausprobieren :)