| |
Fortgeschrittene ProgrammierungStack abbrechen | | | Autor: quickmic | Datum: 20.11.07 12:33 |
| Hi Leute
Ich versuche seit gestern ein besch... Problem in den Griff zu bekommen. Ich habe eine Routine (im original einige Datenbank-Operationen), die relativ Zeitaufwendig sein können. Diesen Code möchte ich bei einem Wiederholungsfall (Stack oder??) abbrechen.
Zur Simulation verwende ich diesen Code:
Option Explicit
Private CancelFlag As Boolean
Private RunnningFlag As Boolean
Private Sub Command1_Click()
Label1 = vbNullString
If RunnningFlag Then
CancelFlag = True
While RunnningFlag
DoEvents
Wend
End If
Call Stacktest
End Sub
Private Sub Stacktest()
Dim i As Long
RunnningFlag = True
For i = 0 To 100000
Label1 = Str(Val(Label1) + 1)
DoEvents
If CancelFlag Then
CancelFlag = False
Exit For
End If
Next
RunnningFlag = False
End Sub Wie man daraus ersehen kann, soll das Sub "Stacktest" abgebrochen werden und neu gestartet, wenn man erneut den Button drückt.
Leider klappt das nicht, da VB im....
While RunnningFlag
DoEvents
Wend ... hängen bleibt.
Ich hab auch schon mit Klassenmodulen getestet . Sowohl normal als auch versucht ein "dynamischen Klassenmodul-Array" zu machen, und das 1.Modul dann "wegzudimensionieren". Das hat dann solala geklappt.... Auch nicht das was ich wollte.
Ebenfalls hab ich's per Classenmodul und "Events" probiert....Wieder Fehlanzeige
Das einzige was geht ist das Teil per Timer zu "entkoppeln".
Das hat aber den Nachteil, dass dann nichtmehr "gewartet" wird, bis der Code beendet ist.
Ich hoffe jemand von euch kann mir bei dem Prob. helfen. Im Mom weiss ich ncihtmehr weiter...
mfg
quickmic | |
Re: Stack abbrechen | | | Autor: quickmic | Datum: 20.11.07 14:01 |
| Genau das ist mein Problem, dass der "Stacktest" mehrmals hintereinander aufgerufen wird (bzw. sein kann). In dem Fall soll der 1.Thread/Stack/Instanz (wie auch immer) abgebrochen werden.
Also genau das:
"...hast du hier das Problem, dass StackTest mehrmals (reentrant) aufgerufen wird"
ABER, das ist nicht mein Problem, sondern genau so beabsichtigt. Das Problem liegt nur darin, dass ich eben den 1. Durchlauf nicht vom 2.Durchlauf aus unterbrechen kann.
Der Bsp. Code ist also schon richtig.
Das "If RunnningFlag Then" kann ja nur gesetzt sein, wenn durch die 1. Instanz noch am laufen ist. Dann wird das CancelFlag gesetzt, und sollte die 1.Instanz korrekt beenden (was es nicht tut, weil VB im Doevents hängt). Dannach soll dann erneut der Stacktest gestartet werden.
Nur um den Sinn darin zu sehen....
Ich hab einige Filter-Auswahlfelder für eine DB die per Klicken auf ein Flexgrid ausgeführt werden. Da meine DB aber ca 100000 Einträge hat, kann das schonmal 4-5Sek dauern. Jetzt will ich aber nicht Alles für die Dauer des Ausführens sperren, sondern es soll zwischenzeitlich eine andere Auswahl getroffen werden können, die die 1.Instanz (vielleicht falsch (Verklickt bei der Auswahl)) abbrechen soll.
mfg
quickmic | |
Re: Stack abbrechen | | | Autor: Zardoz | Datum: 21.11.07 00:15 |
| Hi quickmic,
probiers mal so:
Option Explicit
Private CancelFlag As Boolean
Private RunnningFlag As Boolean, flgNeustart As Boolean
Private Sub Form_Load()
flgNeustart = False
CancelFlag = False
RunnningFlag = False
End Sub
Private Sub Command1_Click()
' Starten
Label1.Caption = ""
If RunnningFlag = False Then
Call StackTest
Else
flgNeustart = True
CancelFlag = True
End If
End Sub
Private Sub Command2_Click()
' Abbruch
flgNeustart = False
CancelFlag = True
End Sub
Private Sub StackTest()
Dim i As Long
RunnningFlag = True
Do
flgNeustart = False
For i = 0 To 100000
Label1.Caption = Str$(i)
DoEvents
If CancelFlag = True Then
CancelFlag = False
Exit For
End If
Next i
Loop While flgNeustart = True
RunnningFlag = False
End Sub Gruss,
Zardoz | |
Re: Stack abbrechen | | | Autor: quickmic | Datum: 21.11.07 08:20 |
| Danke für die Hilfe. Etwas Ähnliches in diese Richtung hab ich ebenfalls schonmal getestet allerdings mit einem Timer. Das System hat aber leider einen Haken:
Beim 1.Mal starten wird im Sub "Command1_Click" gewartet bis "StackTest" fertig durchlaufen ist und dann beendet. So soll es auch sein.
Beim 2.Mal aber wird "Command1_Click" sofort durchlaufen, ohne auf "StackTest" zu warten.
Das ist in dem Bsp nicht schlimm, aber in meinem Fall kommt dannach noch Einiges an Code, welcher erst NACH dem fertigen Durchlaufen des "StackTest" ausgeführt werden darf.
Private Sub Command1_Click()
' Starten
Label1.Caption = ""
If RunnningFlag = False Then
Call StackTest
Else
flgNeustart = True
CancelFlag = True
End If
MsgBox "Hier muss alles fertig sein, denn hier steht noch viel Code"
End Sub Also müsste an der Stelle der Msgbox-Codezeile wieder irgendeine "Warteroutine" rein. z.b.
Private Sub Command1_Click()
' Starten
Label1.Caption = ""
If RunnningFlag = False Then
Call StackTest
Else
flgNeustart = True
CancelFlag = True
End If
Do While RunningFlag
DoEvents
Loop
MsgBox "Hier muss alles fertig sein, denn hier steht noch viel Code"
End Sub ....und das Resultat ist das selbe wie bei meinem ersten Bsp. VB "hängt" im Doevents.
mfg
quickmic | |
Re: Stack abbrechen | | | Autor: Dirk | Datum: 21.11.07 11:17 |
| DoEvents ist dafür da, die Kontrolle ans Betriebssystem zurück zu geben, um asynchrone Ereignisse wie Maus- und TastaturEvents zu verarbeiten. Wenn dein StackTest noch nicht zu Ende gelaufen ist und du "mittendrin" in deine Button Ereignisprozedur springst, ist damit StackTest unterbrochen und wird erst wieder weiterlaufen, nachdem Command1_Click fertig ist. Das wird aber niemals fertig, da StackTest nicht in der Lage ist - weil unterbrochen - das RunningFlag auf False zu setzen.
Entweder beschäftigst du dich mit Threads oder du änderst dein Design.
Packe mal folgende Zeilen in deinen Button-Handler und die Funktion TestStack Static Indent As Integer
Indent = Indent + 1
Debug.Print String(c_IND * Indent, ">") & " Command1_Click" 'bzw. testStack
'Hier kommt der Code
Debug.Print String(c_IND * Indent, "<") & " Command1_Click"
Indent = Indent - 1 Hier kannst du sehr gut die Aufruf-Hierachie an den Printouts nachvollziehen. Du wirst also feststellen, das der Aufruf von TestStack synchrin erfolgt, dass heißt, du kommst erst in deinen Button-Handler zurück, wenn TestStack fertig ist. Der wird aber nicht fertig, weil du die while-loop im zweiten Aufruf von Command1_Click nudelt.
Ich stelle mir daher folgende Lösung vor:
Option Explicit
Private Const c_IND As Integer = 3
Private m_Break As Boolean
Private m_NeuAuftrag As Boolean
Private m_IsAuftragRunning As Boolean
Private m_CloseApp As Boolean
Private Sub Command1_Click()
Static Indent As Integer
Indent = Indent + 1
Debug.Print String(c_IND * Indent, ">") & " Command1_Click"
If m_IsAuftragRunning Then
m_Break = True
m_NeuAuftrag = True
End If
Timer.Interval = 1
Timer.Enabled = True
Debug.Print String(c_IND * Indent, "<") & " Command1_Click"
Indent = Indent - 1
End Sub
Private Sub test()
Dim i As Long
Static Indent As Integer
Indent = Indent + 1
Debug.Print String(c_IND * Indent, ">") & " test"
For i = 1 To 1000000
If i Mod 10 = 0 Then
If m_Break Then
Debug.Print String(c_IND * (Indent + 1), ".") & " BREAK-->"
Exit For
End If
Label1.Caption = CStr(i)
DoEvents
End If
Next i
If Not m_Break Then Debug.Print String(c_IND * (Indent + 1), ".") & " MADE IT" & _
"!!!"
Debug.Print String(c_IND * Indent, "<") & " test"
Indent = Indent - 1
End Sub
Private Sub Form_Load()
m_Break = False
Timer.Interval = 1
Timer.Enabled = False
m_NeuAuftrag = False
m_IsAuftragRunning = False
m_CloseApp = False
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
m_Break = True
m_NeuAuftrag = False
m_CloseApp = True
If m_IsAuftragRunning Then
Cancel = 1
End If
End Sub
Private Sub Timer_Timer()
Static Indent As Integer
Indent = Indent + 1
Debug.Print String(c_IND * Indent, ">") & " Timer_Timer"
Timer.Interval = 500
If m_IsAuftragRunning Then
Timer.Enabled = False
Else
m_IsAuftragRunning = True
test
m_IsAuftragRunning = False
If m_CloseApp Then
SendKeys "%{F4}", False
DoEvents
End If
If m_NeuAuftrag And m_Break Then
m_NeuAuftrag = False
Timer.Enabled = True
Else
Timer.Enabled = False
End If
m_Break = False
End If
Debug.Print String(c_IND * Indent, "<") & " Timer_Timer"
Indent = Indent - 1
End Sub Bei mir scheint's zumindest zu funktionieren. Jetzt schreibst du, dass da irgendwo noch ganz viel Code kommt. Grundsätzlich ungünstig viel Code in einer Ereignisprozedur zu haben. Besser auslagern in eine Sub/Funktion. Du kannst ja nun noch ein Flag setzen, dass TestStack oder hier test es gemacht hat und dann entsprechend den Code ablaufen lassen.
Ich denke ohne Timer kriegste das nicht hin. Denn du willst ja so eine Art Threading erreichen. App beenden bzw. Form schließen habe ich noch extra verarbeitet.
Gruß
Dirk
--
?Get it right the first time | |
Re: Stack abbrechen | | | Autor: quickmic | Datum: 21.11.07 12:26 |
| "DoEvents ist dafür da, die Kontrolle ans Betriebssystem zurück zu geben, um asynchrone Ereignisse wie Maus- und TastaturEvents zu verarbeiten" ... Leuchtet ein. Darum geht auch die Timer-Methode, weil das Timer-Event vom OS kommt.
Dein Bsp klappt auch bei mir, aber eben leider genau wie ich es nicht brauchen kann.
Warum das ganze mit dem Doevents nicht klappen kann, ist mir nun auch klar. Sowas in die Richtung hab ich ja bereits vermutet.
Leider bin ich jetzt etwas ratlos...
Du meinst entweder Code (Timer) ändern, oder Threads machen.
"Besser auslagern in eine Sub/Funktion. Du kannst ja nun noch ein Flag setzen, dass TestStack oder hier test es gemacht hat und dann entsprechend den Code ablaufen lassen."
Der Code steht natürlich bereits in einer Sub/Function drin, aber er wird von mehreren Stellen im Code und immer wieder in etwas modifizierter Form, benötigt. Ich kann da also keinen Timer einbauen, der mir nach Schema F den 2.Teil des Codes ausführt.
Ausserdem wird das ganze dann unglaublich unübersichtlich und hässlich.
z.b.
Einmal benötige ich einen Stack bei den SQL-Kommandos-Unterprogramm (Da sind dann die Auswahlfilter drin). Das kann z.b. mehrere Sek dauern bis die Recordsets geladen sind, und 2. Beim Füllen des Flexgrids, was wieder einige Sek dauern kann.
Ich kann daher eigentlich die Timer-Methode schon nahezu ausschliessen.
Bleibt also noch die Methode mit den Threads.
Leider erinnere ich mich mit Grauen an den letzten Versuch mit VB Threads hinzubekommen. Diese Methode möchte ich daher nur im Absoluten Notfall einsetzen.
Ich möchte aber nochmal vielleicht auf die Klassenmodul-Methode zurückkommen, die ich am Anfang kurz erwähnt hab.
Vielleicht ist ja mit diesem Weg mehr anzufangen, zumindest hatte ich bis jetzt den "grössten" Erfolg mit diesem Code:
Im Form1:
Option Explicit
Private class1() As class1
Private counter As Integer
Private Sub Command1_Click()
If (Not Not class1) <> 0 Then 'Array dimensioniert?
class1(0).cancel
Set class1(0) = Nothing
Erase class1
ReDim class1(0)
Set class1(0) = New class1
class1(0).Stacktest
Else
ReDim class1(0)
Set class1(0) = New class1
class1(0).Stacktest
Set class1(0) = Nothing
Erase class1
End If
MsgBox "Durchlauf fertig"
End Sub Im Class1:
Option Explicit
Private cancelflag As Boolean
Private RunnningFlag As Boolean
Public Sub cancel()
cancelflag = True
End Sub
Public Sub Stacktest()
Dim i As Long
RunnningFlag = True
For i = 0 To 10000
Form1.Label1 = Str(i)
DoEvents
If cancelflag Then
cancelflag = False
Exit For
End If
Next
RunnningFlag = False
End Sub Das Problem hier ist, dass zwar "Stacktest" im Klassenmodul abgebrochen (ich würde eher sagen abgewürgt) wird, aber das 1.Button-Event nicht sofort beim abwürgen beendet wird.
Kann man da noch was optimieren, oder bleibt wirklich nur der Weg mit den Threads?
mfg
quickmic | |
| Sie sind nicht angemeldet! Um auf diesen Beitrag zu antworten oder neue Beiträge schreiben zu können, müssen Sie sich zunächst anmelden.
Einloggen | Neu registrieren |
|
|
Neu! sevCommand 4.0
Professionelle Schaltflächen im modernen Design!
Mit nur wenigen Mausklicks statten auch Sie Ihre Anwendungen ab sofort mit grafischen Schaltflächen im modernen Look & Feel aus (WinXP, Office, Vista oder auch Windows 8), inkl. große Symbolbibliothek. Weitere InfosTipp des Monats Access-Tools Vol.1
Über 400 MByte Inhalt
Mehr als 250 Access-Beispiele, 25 Add-Ins und ActiveX-Komponenten, 16 VB-Projekt inkl. Source, mehr als 320 Tipps & Tricks für Access und VB
Nur 24,95 EURWeitere Infos
|