Hallo zusammen,
ich habe in Visual Basic 2015 eine For-Schleife, deren vollständige
Abarbeitung ca. 20 min dauert. Nun würde ich diese gern durch ein
Ereignis außerhalb der Schleife, wie einen Button, vorzeitig beenden.
Solange die Schleife aber aktiv ist, kann ich leider keinen Button
betätigen.
Wie könnte man dies lösen oder ist eine Do-While-Schleife besser
geeignet?
Danke
Du musst natürlich Events (oder wie die in Visual BAsic heissen mögen)
zulassen.
Dann kann dein Button auch ein Flag setzen, das du innerhalb deiner
for-Schleife auswertest und bei ggf. die for-Schleife abbricht.
Danke für den Hinweis "STK500-Besitzer" aber Visual Basic ist an sich
schon ereignisgesteuert. Ich brauche so etwas wie einen Interrupt wie
z.B. bei den Atmegas, bei denen ich durch eine Unterbrechung eine
Variable innerhalb der Schleife setzen kann.
Denke mit DoEvents komme ich vielleicht weiter. Kannte ich noch nicht!
Danke Thomas
Moin!
Dein Fehler ist, die lange laufende Operation auf dem UI-Thread ablaufen
zu lassen. Lass sie in einem separaten Thread laufen, dann kannst du
auch andere Buttons drücken.
Beachte, dass UI-Operationen, wie das Aktualisieren eines TextBox-Textes
während der langen Operation, dann über eine spezielle Invoke-Funktion
wieder mit dem UI-Thread synchronisiert werden müssen.
Das erste Beispiel hilft dir schon weiter:
https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/operators/await-operator
dim Abbruch as Boolean = false ' in global-bas deklarien.
abbruch = false
For i = 1 to ganz_viele
i = i+1
my.application.doevents
if abbruch = true then exit for
next i
Sub button_click
abbruch = true
end sub
FERTIG.
Tippfehler unterliegen den Copyright. ;)
JBourges schrieb:> Funktioniert! Muss allerdings den Abbruch-Button zweimal drücken, warum> auch immer!
Weil da immer noch eine Zeitschleife drin ist. Die bekommst du nicht
wirklich raus, weil das Click selbst erst abgefragt wird wenn der
DOEVENT-Befehl kommt.
z.b.
For i = 1 to ganz_viele
for ii = 1 to 10000
x= x + 1
next ii
my.application.doevents ' <- wird erst abgefragt und ausgeführt wenn
x um 10000 größer ist.
next i
Einfach gesagt. Überlege dir wo du das positionierst. Weil dein
Click-Ereignis wird NICHT übersehen sondern gepuffert. Du kannst es
Theoretisch nach jeden Befehl machen aber das geht dann auf die
allgemeine Ablaufgeschwindigkeit, und der Befehl verbraucht einiges an
Zeit. Nanosekunden sind auch ein Zeiteinheit. !!! ;)
Es reicht also i.d.R. 1 Klick.
Allerdings ist VB nicht sehr gut darin 2 Dinge gleichzeitig zu tun. Ergo
wird so ein Click schon mal übersehen,wenn gerade eine andere Routine
läuft. Aber das Problem ist Windows-Like und hat nix mit VB selbst zu
tun.
Machen andere Programme auch so. Alles das selbe. Programmiersprache ist
bei den Thema egal.
Windows ist halt kein Multitasking-System sondern ein
Zeitschleifen-System. Was bedeutet: Innerhalb einer Zeiteinheit wird
jeden Prozess eine bestimmte Anzahl von Zeit zugeordnet. Und innerhalb
dieser zugeordneten Zeit darf er was machen ansonsten macht er NIX.
Kleiner Nachtrag :
Ich nutze den Befehl so meist.
For i = 1 to ganz_viele
y = y+1
if y = 20 then
my.application.doevents
y = 0
label_1.text = str(i)
end if
next i
Das hält die Bildschirmausgabe am laufen und ich kann den Zähler
ablesen, was mir je nach Prg. sagt wie weit der ist. Kannst du auch
modern machen, in den du ein Ablaufbalken nutzt. Dann muss du in den
Falle i als Prozentzahl zu 100 übergeben an den Balken.
Das macht man alles nicht so... Mach es genauso wie in c#... Starte
einen task und gib dem ein cacellationtoken mit... In der Schleife
fragst du ab ob dieses ausgelöst wurde und wenn ja beendet den task
Ja, danke für die Info. Das Zeitscheiben-Problem in Windows ist mir
bekannt. Das zwei- oder mehrmalige Drücken auf den Button ist
hinnehmbar. Zeitprobleme habe ich nicht, da ich zum Senden an den
Mikrocontroller sowieso eine Wartezeit von 200ms drin habe.
Für das Auffrischen und Anzeigen der Laufvariable in der Schleife nehme
ich ganz gern den Befehl: Label7.Update()
Gruß aus MD
JBourges schrieb:> Danke für den Hinweis "STK500-Besitzer" aber Visual Basic ist an sich> schon ereignisgesteuert
Sehr gerne.
Visual Basic ansich ist event-gesteuert. Dein Programm aber scheinbar
nicht.
Aber glücklicherweise haben sich andere um die Lösung bemüht.
JBourges schrieb:> Für das Auffrischen und Anzeigen der Laufvariable in der Schleife nehme> ich ganz gern den Befehl: Label7.Update()> Gruß aus MD
JA, aber das bringt nur was wenn die Form am Schirm ist. Wenn du das
Prg. minimierst und wieder normalisierst wird NICHT die ganze Form neu
gezeichnet solange die Sub läuft. Bei dein .update wird nur das Feld
geupdatet.
Deshalb löse ich das doevents über die Application aus. Was bedeutet :
"Zeichne alles neu". Mir ist das wichtig da viele meiner Prg. Teilweise
Stunden laufen, und da will ich halt nicht die Form am Schrim habe, aber
mal nach gucken wie weit sie ist.
Das führt sogar soweit das ich für eine MSGBOX vorher den Name der Form
ändere. Dann steht in meiner Taskleiste "hp:Frage" und ich weiß das
irgendwo jemand was von mir will. Ist besonders wichtig wenn ich ein
COPY-DATEI Befehl sende der mal eben 4-5 GB über das 100 Mbit
Interne-Netzwerk schaufelt.
Schlaumaier schrieb:> Es reicht also i.d.R. 1 Klick.
Das ist richtig. Input-Events werden gepuffert.
> Allerdings ist VB nicht sehr gut darin 2 Dinge gleichzeitig zu tun. Ergo> wird so ein Click schon mal übersehen,wenn gerade eine andere Routine> läuft.
Nein. Dann wäre etwas im Programm falsch. I.d.R. wohl: sinnloses Warten.
D.h.: das gepufferte Event kann nicht wirksam werden, weil der
Main-Thread es überhaupt nicht abfragt, weil er anderweitig beschäftigt
ist (busy-loop) oder sogar auf Anweisung des Programmierers selber
erklärt hat, erstmal eine Weile überhaupt nichts tun zu wollen
(thread.sleep()).
> Aber das Problem ist Windows-Like und hat nix mit VB selbst zu> tun.
Es hat tatsächlich nix mit der verwendeten Programmiersprache zu tun,
aber auch nix mit Windows. Ist vielmehr im Prinzip für praktisch alle
grafischen Oberflächen so. Überall gibt es es ein Messaging-System und
praktisch überall wird es aus Performance-Gründen synchronisiert und
dann serialisiert in einem GUI-Thread abgearbeitet.
Der Sinn dieser Synchonisation und Serialisierung ist einfach: den Code
erstens nicht extrem anschwellen zu lassen und zweitens nicht die
Möglichkeit für unzählige Deadlock-Situationen quasi vorzuprogrammieren.
Wiederum ebenfalls in praktisch allen GUI-Systemen gibt es
Möglichkeiten, da irgendwie ein wenig drumherum zu programmieren.
Application.DoEvents ist eine davon, bei anderen Spachen/GUI-Systemen
gibt es fast immer die entsprechenden Equivalente.
Ist aber alles Gülle, einzig (scheinbar) nützlich für faule und dumme
Programmierer. Der einzige wirklich richtige Weg ist hingegen immer:
langdauernde Aufgaben außerhalb des Haupthread verarbeiten, in einem
Extra-Thread. Das wirft auch schon genug Probleme auf, ist aber am Ende
immer noch deutlich leichter zu beherrschen als Pfusch-Code, der z.B.
mit Application.DoEvents oder vergleichbarem Müll hantiert.
Bei .Net gibt es für diesen Zweck übrigens schon den BackgroundWorker
als fertige Komponente. Der enthält fast alles, was nötig ist, um eine
langdauernde Aufgabe sinnvoll parallel zum MainThread abzuarbeiten.
Auch unter Berücksichtung folgender Möglichkeiten/Notwendigkeiten:
1) der parallele Thread muss vorzeitig abgebrochen werden
2) der parallele Thread stößt auf eine nicht behebbare Fehlersitation
Was er allerdings leider nicht enthält, ist eine einfache Möglichkeit
zur Synchronisation mit dem GUI-Thread. Das muss man "von Hand" machen.
Diese scheinbare Schwäche ist einfach dadurch begründet, dass dieser
Worker nicht nur dafür gedacht ist, Threads parallel zu einem GUI-Thread
abzuarbeiten, sondern generell dazu, konkurrierende Threads
abzuarbeiten, die u.U. überhaupt nix mit dem GUI zu schaffen haben. Die
müssen natürlich auch synchronisiert werden, aber eben oft nur
untereinander. Dazu sind aber andere Mechanismen nötig/sinnvoll als zur
Synchronisierung mit dem GUI-Thread.
@ c-hater
Ich gebe dir in allen Recht. Schön erklärt.
Aber glaubst du ernsthaft das jemand der nicht einmal ein Doevents kennt
in der Lage ist mit so Techniken zu arbeiten. Ich persönlich löse das
Ereignis nur alle X Runden in einer Schleife aus. Und ich habe noch nie
ein Programm ausliefern müssen wo ich so Probleme hatte.
Anders ist das auf meinen Rechner. Da sind so Operationen fast an der
Tagesordnung aber da es nur auf mein Rechner läuft mache ich mir nicht
die Arbeit es "edel" zu machen. Das soll schnell gehen, und die von dir
genannten Methoden machen es nicht wirklich schneller nur etwas "edler".
Und viele meiner Progamme habe für die Einstellungen nur Variablen die
im Prg-Code drin sind. Geht schneller als erst mal 2 Objekte zu malen,
platzieren und mit Eigenschaften zu füllen. Und eine Lade-Routine die
dann diese Werte permanent speichert. ;) Ich bin halt faul.
Ich trenne klar zwischen : Kann ICH benutzen und "Das Programm muss
DAU-Sicher sein". DAU-Sicher machen dauert i.d.R. 80% der
Entwicklungszeit.
Hallo Schlaumaier,
Schlaumaier schrieb:> For i = 1 to ganz_viele> y = y+1> if y = 20 then> my.application.doevents> y = 0> label_1.text = str(i)> end if> next i
ich hab mir mal Deinen Code weggespeichert, weil er ein schönes kleines
Beispiel für häßlichen Code darstellt, mit drei Auffälligkeiten in nur
acht Zeilen.
1. Erst einmal benutze ich "i" nicht, das wird zu leicht verwechselt.
2. Die Abfrage auf jeden zwangzigsten Durchlauf kann man eleganter und
lesbarer darstellen.
3. Es nützt nichts, ein GUI-Element zu beschreiben, nachdem man den
Bildschirm aktualisiert hat - das wird im Zweifelsfall gar nicht
sichtbar.
Es geht auch lesbarer:
for x=1 to anzahl
if (x mod 20)=0 then
label_1.text = str(x)
my.application.doevents
end if
next x
Peter M. schrieb:> 1. Erst einmal benutze ich "i" nicht, das wird zu leicht verwechselt.> 2. Die Abfrage auf jeden zwangzigsten Durchlauf kann man eleganter und> lesbarer darstellen.> 3. Es nützt nichts, ein GUI-Element zu beschreiben, nachdem man den> Bildschirm aktualisiert hat - das wird im Zweifelsfall gar nicht> sichtbar.Zu 1.
ES ist MEIN Code. Und ich benutze i seit 30 Jahren als
Schleifen-Variable oder als Zähl-Variable. Und ich kann meine
uralt-Codes vor 30 Jahren noch lesen sofern ich darauf Zugang habe. Ich
weiß nicht ob die Cassetten des ZX-81 noch lesebar sind.
Davon abgesehen habe ich auf Systemen/Sprachen gelernt wo es kein Mode
befehl gibt.
Ich weiß nicht einmal ob B4* einen Mod-Befehl unterstützt. Und das ist
das Entwicklungssystem was ich für Android und Arduino/Raspberry nutze.
Wenn man nämlich nach den kleinsten gemeinsamen Nenner codete, hat man
den Vorteil das man sich weniger Unterschiede merken muss. Mich regt
schon auf das ich bei b4* nicht die Leertaste sondern Return zum
auswählen einer Eigenschaft im Pulldown nehmen muss.
zu 2. Ich hasse eleganten Code. Der Code muss nicht elegant sein, weil
das ja so modern ist. Ein Code muss LESBAR sein wie ein Roman. Schnell
und ohne das ich dabei denken muss.
zu 3 Da gebe ich dir völlig recht. So was passiert wenn man auf die
Schnelle in einen Forum ein Code runter nagelt. Selbstverständlich muss
das DOEVENTS am ende der Abfrage stehen.
Hallo Schlaumaier,
Schlaumaier schrieb:> zu 2. Ich hasse eleganten Code. Der Code muss nicht elegant sein, weil> das ja so modern ist. Ein Code muss LESBAR sein wie ein Roman. Schnell> und ohne das ich dabei denken muss.
Du darfst das "elegant" gerne durch "lesbar" ersetzen!
Deine geschachtelte Schleife halte ich (Du magst das gerne anders sehen)
für weniger lesbar als eine einzige Schleife.
Hallo auch,
denke das Forum wird immer ....
Schon die erste Antwort von "STK500-Besitzer (Gast)" war nichtssagend.
Und dann wird gestritten, wer besser (oder sauberer ... eher nicht)
programmiert. Die Antwort von Thomas war konkret und weiterführend
genug.
Es ist doch völlig unerheblich ob Modula besser ist oder zwei Zeilen
mehr verständlicher.
Nur weiter so .....
Gruß von der Küste
Eberhart