KillThread

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
Daffy0815
Beiträge: 390
Registriert: 15.06.2005 00:44
Wohnort: 65719 Hofheim
Kontaktdaten:

KillThread

Beitrag von Daffy0815 »

Hallo Leute,

in der Dokumentation habe ich gelesen, das man möglichst von der Verwendung der KillThread-Anweisung absehen soll.

Ich habe einen Thread mit jeder Menge nacheinander angeordneten IF-ENDIF / verschachtelten While-Wend.
Wird der Thread komplett durchlaufen so ist er auch beendet.

Jetzt gibt es im Hauptprogramm einen Button mit dem man den Thread jederzeit beenden kann.
Die einfachste Lösung dafür wäre "KillThread" als Reaktion auf die Betätigung des Buttons.

Nach meinem Kentnisstand wäre die einzige "KillThread-freie-Lösung" eine Globalvariable die an zig Stellen innerhalb des Threads abgefragt werden müsste um diesen Thread zu beenden.

Gibt es da auch eine einfachere Lösung?

Gruß

Daffy
Wir sind LINUX
Widerstand ist zwecklos - Sie werden emuliert
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: KillThread

Beitrag von ts-soft »

Hier ein Beispiel ohne Globale Variable!
Es wird eine Semaphore als Signal verwendet.

Hier noch ein verkürztes Beispiel:

Code: Alles auswählen

EnableExplicit

Procedure MyThread(semaphore)
  Protected time = ElapsedMilliseconds() + 10000
  Protected loop = 0
  Repeat
    If time > ElapsedMilliseconds()
      Debug loop
      time = ElapsedMilliseconds() + 10000
    EndIf
    loop + 1
    Delay(1)
  Until TrySemaphore(semaphore)
EndProcedure

OpenWindow(0, #PB_Ignore, #PB_Ignore, 140, 80, "bla")
ButtonGadget(0, 5, 5, 100, 20, "start")
ButtonGadget(1, 5, 30, 100, 20, "stop")
DisableGadget(1, #True)
Define semaphore = CreateSemaphore()
Define thread
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          DisableGadget(0, #True)
          thread = CreateThread(@MyThread(), semaphore)
          DisableGadget(1, #False)
        Case 1
          SignalSemaphore(semaphore)
          WaitThread(thread, 2000)
          DisableGadget(1, #True)
          DisableGadget(0, #False)
      EndSelect
  EndSelect
ForEver
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Daffy0815
Beiträge: 390
Registriert: 15.06.2005 00:44
Wohnort: 65719 Hofheim
Kontaktdaten:

Re: KillThread

Beitrag von Daffy0815 »

@ts-soft

Schönes Beispiel für die Verwendung von Semaphore aber leider keine Lösung für mein Problem.

Fügen Sie doch mal vor "loop +1" (wusste gar nicht das das geht) "While 0=0 : Wend" ein und versuchen Sie dann mal den Thread zu beenden.
Das ginge nur wenn zwischen While und Wend eine Abfrage wäre die dann mit Break die Schleife verlässt.

Gibt es denn wenigstens so etwas wie "ExitProcedure" ?
(Könnte man natürlich mit dem allseits verpönten "GoTo" zum Ende der Procedur lösen)

Gruß

Daffy
Zuletzt geändert von Daffy0815 am 04.03.2012 18:55, insgesamt 1-mal geändert.
Wir sind LINUX
Widerstand ist zwecklos - Sie werden emuliert
Benutzeravatar
RSBasic
Admin
Beiträge: 8047
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: KillThread

Beitrag von RSBasic »

ProcedureReturn
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Benutzeravatar
Daffy0815
Beiträge: 390
Registriert: 15.06.2005 00:44
Wohnort: 65719 Hofheim
Kontaktdaten:

Re: KillThread

Beitrag von Daffy0815 »

@RSBasic

Aha, ich dachte das wäre nur die Übergabe des Rückgabewertes.

Danke!

Gruß

Daffy
Wir sind LINUX
Widerstand ist zwecklos - Sie werden emuliert
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: KillThread

Beitrag von ts-soft »

Verstehe das Problem nicht, wäre dann logischerweise:

Code: Alles auswählen

While TrySemaphore(semaphore) = 0 : Wend
Ansonsten kann man, wie bereits gesagt, mit ProcedureReturn
jede Procedure beenden, wobei dies bei Treads sehr unüblich ist.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Daffy0815
Beiträge: 390
Registriert: 15.06.2005 00:44
Wohnort: 65719 Hofheim
Kontaktdaten:

Re: KillThread

Beitrag von Daffy0815 »

Das mit dem ProcedureReturn hat auch nicht vernünftig funktioniert!
Beim erneuten Start des Threads kam es zu sporadischen Austeigern aus dem Programm.

Ich habe es jetzt halt doch auf die Art mit den tausend Abfragen gemacht (was ich persönlich doof finde - Ich liebe Assembler!)

Hier das reale Beispiel (nicht lauffähig):

Code: Alles auswählen

#WartezeitMinMaxdruck = 5000 ;mSek
#AufloesungDAWandler = 16384 ;Bit

Procedure ErzeugeDrucktabelleThread(*Dummy)
    ;
    Debug "DrucktabelleThread"    
    ExitDrucktabelleThread.i = #False
    IstdruckRefresh.i = #False
    SysParDrucktabelleGueltig.d = #False
    SetSysPar(#RegSysParDrucktabelleGueltig, SysParDrucktabelleGueltig.d)
    SetDOut(#AbsperrventilY3, 1) ;schließen
    SetDOut(#PruefdruckventilY2, 1) ;öffnen
    ;
    IndexDrucktabelle.i = 0
    SolldruckStart.d = MinDruckDrucksystem.d 
    SolldruckEnde.d = MaxDruckDrucksystem.d
    SolldruckStufung.d = Pow(10, -#AnzahlNachkommastellenDrucksystem)
    ;
    StelldruckStart.d = SysParAnOut00Min.d
    StelldruckEnde.d =  SysParAnOut00Max.d
    StelldruckStufung.d = (StelldruckEnde.d - StelldruckStart.d) / #AufloesungDAWandler
    ;
    Debug "SolldruckStart____="+StrD(SolldruckStart.d)
    Debug "SolldruckEnde_____="+StrD(SolldruckEnde.d)
    Debug "SolldruckStufung__="+StrD(SolldruckStufung.d)
    ;
    If ExitDrucktabelleThread.i <> #True
        If SolldruckStart.d >= 0 And SolldruckEnde.d > 0  ;Nur Überdruck
            Debug "Nur Überdruck" 
            SetDOut(#VakuumventilY8, 0)
            ;
            Debug "Maxdruck prüfen"
            SolldruckMax.d = SolldruckEnde.d
            SetSolldruck(SolldruckMax.d)
            SetStelldruck(StelldruckEnde.d)
            Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
            While Istdruck.d < SolldruckMax.d
                If ExitDrucktabelleThread.i = #True : Break : EndIf
                If ElapsedMilliseconds() > Wait.i
                    RaiseError(#ERROR_MaxDruckNichtErreicht)
                EndIf
                ;Delay(#DelayDrucktabelleThread)                    
                Istdruck.d = GetIstdruck()
            Wend
            If ExitDrucktabelleThread.i <> #True
                Debug "MaxDruck OK"
                Debug "Mindruck prüfen"
                SolldruckMin.d = SysParDruckgrenzePositiv.d + SolldruckStufung.d
                SetSolldruck(SolldruckMin.d)
                SetStelldruck(StelldruckStart.d)
                Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
                While Istdruck.d > SolldruckMin.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    If ElapsedMilliseconds() > Wait.i 
                        RaiseError(#ERROR_MinDruckNichtErreicht)
                    EndIf
                    ;Delay(#DelayDrucktabelleThread)                    
                    Istdruck.d = GetIstdruck()
                Wend
            EndIf
            If ExitDrucktabelleThread.i <> #True
                Debug "Mindruck OK"
                SolldruckStart.d = SysParDruckgrenzePositiv.d + SolldruckStufung.d
                Solldruck.d = SolldruckStart.d
                Stelldruck.d = StelldruckStart.d
                While Solldruck.d < SolldruckEnde.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    SetSolldruck(Solldruck.d)
                    If Istdruck.d > Solldruck.d : RaiseError(#ERROR_IstdruckGroesserSolldruck) : EndIf
                    If ExitDrucktabelleThread.i <> #True
                        While Istdruck.d < Solldruck.d
                            If ExitDrucktabelleThread.i = #True : Break : EndIf
                            Stelldruck.d = Stelldruck.d + StelldruckStufung.d 
                            ;Debug "Stelldruck________="+StrD(Stelldruck.d)
                            If Stelldruck.d > StelldruckEnde.d : RaiseError(#ERROR_SolldruckNichtErreichbar) : EndIf 
                            SetStelldruck(Stelldruck.d)
                            ;Delay(#DelayDrucktabelleThread)                    
                            Istdruck.d = GetIstdruck()
                        Wend
                    EndIf
                    If ExitDrucktabelleThread.i <> #True
                        ;Hier Stelldruck speichern
                        Drucktabelle.w(IndexDrucktabelle.i) = Round(Stelldruck.d * 1000, #PB_Round_Nearest)
                        Debug "StelldruckInt_____="+Str(Drucktabelle.w(IndexDrucktabelle.i))
                        IndexDrucktabelle.i = IndexDrucktabelle.i + 1
                        ;
                        Solldruck.d = Solldruck.d + SolldruckStufung.d
                    EndIf
                Wend
            EndIf
        EndIf
    EndIf    
    ;
    ;
    ;
    If ExitDrucktabelleThread.i <> #True
        If SolldruckStart.d < 0 And SolldruckEnde.d <= 0  ;Nur Unterdruck
            Debug "Nur Unterdruck" 
            SetDOut(#VakuumventilY8, 1)
            ;
            Debug "Mindruck prüfen"
            SolldruckMin.d = SolldruckStart.d
            SetSolldruck(SolldruckMin.d)
            SetStelldruck(StelldruckEnde.d)
            Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
            While Istdruck.d > SolldruckMin.d
                If ExitDrucktabelleThread.i = #True : Break : EndIf
                If ElapsedMilliseconds() > Wait.i 
                    RaiseError(#ERROR_MinDruckNichtErreicht)
                EndIf
                ;Delay(#DelayDrucktabelleThread)                    
                Istdruck.d = GetIstdruck()
            Wend
            Debug "Mindruck OK"
            If ExitDrucktabelleThread.i <> #True
                Debug "Maxdruck prüfen"
                SolldruckMax.d = SysParDruckgrenzeNegativ.d - SolldruckStufung.d
                SetSolldruck(SolldruckMax.d)
                SetStelldruck(StelldruckStart.d)
                Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
                While Istdruck.d < SolldruckMax.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    If ElapsedMilliseconds() > Wait.i
                        RaiseError(#ERROR_MaxDruckNichtErreicht)
                    EndIf
                    ;Delay(#DelayDrucktabelleThread)                    
                    Istdruck.d = GetIstdruck()
                Wend
            EndIf
            If ExitDrucktabelleThread.i <> #True
                Debug "MaxDruck OK"
                SolldruckStart.d = SysParDruckgrenzeNegativ.d - SolldruckStufung.d
                Solldruck.d = SolldruckStart.d
                Stelldruck.d = StelldruckStart.d
                While Solldruck.d < SolldruckEnde.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    SetSolldruck(Solldruck.d)
                    If Istdruck.d < Solldruck.d : RaiseError(#ERROR_IstdruckKleinerSolldruck) : EndIf
                    If ExitDrucktabelleThread.i <> #True
                        While Istdruck.d > Solldruck.d
                            If ExitDrucktabelleThread.i = #True : Break : EndIf
                            Stelldruck.d = Stelldruck.d + StelldruckStufung.d 
                            ;Debug "Stelldruck________="+StrD(Stelldruck.d)
                            If Stelldruck.d > StelldruckEnde.d : RaiseError(#ERROR_SolldruckNichtErreichbar) : EndIf 
                            SetStelldruck(Stelldruck.d)
                            ;Delay(#DelayDrucktabelleThread)                    
                            Istdruck.d = GetIstdruck()
                        Wend
                    EndIf
                    If ExitDrucktabelleThread.i <> #True
                        ;Hier Stelldruck speichern
                        Drucktabelle.w(IndexDrucktabelle.i) = Round(Stelldruck.d * 1000, #PB_Round_Nearest) * -1
                        Debug "StelldruckInt_____="+Str(Drucktabelle.w(IndexDrucktabelle.i))
                        IndexDrucktabelle.i = IndexDrucktabelle.i + 1
                        ;
                        Solldruck.d = Solldruck.d - SolldruckStufung.d
                    EndIf
                Wend
            EndIf
        EndIf     
    EndIf    
    ;
    ;
    ;
    If ExitDrucktabelleThread.i <> #True
        If SolldruckStart.d < 0 And SolldruckEnde.d > 0  ;Unterdruck und Überdruck
            Debug "Unterdruck und Überdruck" 
            SetDOut(#VakuumventilY8, 1)
            ;
            Debug "Mindruck prüfen"
            SolldruckMin.d = SolldruckStart.d
            SetSolldruck(SolldruckMin.d)
            SetStelldruck(StelldruckEnde.d)
            Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
            While Istdruck.d > SolldruckMin.d
                If ExitDrucktabelleThread.i = #True : Break : EndIf
                If ElapsedMilliseconds() > Wait.i 
                    RaiseError(#ERROR_MinDruckNichtErreicht)
                EndIf
                ;Delay(#DelayDrucktabelleThread)                    
                Istdruck.d = GetIstdruck()
            Wend
            If ExitDrucktabelleThread.i <> #True
                Debug "Mindruck OK"
                SetDOut(#VakuumventilY8, 0)
                Debug "Maxdruck prüfen"
                SolldruckMax.d = SolldruckEnde.d
                SetSolldruck(SolldruckMax.d)
                SetStelldruck(StelldruckEnde.d)
                Wait.i = ElapsedMilliseconds() + #WartezeitMinMaxdruck
                While Istdruck.d < SolldruckMax.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    If ElapsedMilliseconds() > Wait.i
                        RaiseError(#ERROR_MaxDruckNichtErreicht)
                    EndIf
                    ;Delay(#DelayDrucktabelleThread)                    
                    Istdruck.d = GetIstdruck()
                Wend
            EndIf
            If ExitDrucktabelleThread.i <> #True
                Debug "MaxDruck OK"
                SetDOut(#VakuumventilY8, 1)
                SolldruckStart.d = SysParDruckgrenzeNegativ.d - SolldruckStufung.d
                Solldruck.d = SolldruckStart.d
                Stelldruck.d = StelldruckStart.d
                While Solldruck.d < SolldruckEnde.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    SetSolldruck(Solldruck.d)
                    If Istdruck.d < Solldruck.d : RaiseError(#ERROR_IstdruckKleinerSolldruck) : EndIf
                    If ExitDrucktabelleThread.i <> #True
                        While Istdruck.d > Solldruck.d
                            If ExitDrucktabelleThread.i = #True : Break : EndIf
                            Stelldruck.d = Stelldruck.d + StelldruckStufung.d 
                            ;Debug "Stelldruck________="+StrD(Stelldruck.d)
                            If Stelldruck.d > StelldruckEnde.d : RaiseError(#ERROR_SolldruckNichtErreichbar) : EndIf 
                            SetStelldruck(Stelldruck.d)
                            ;Delay(#DelayDrucktabelleThread)                    
                            Istdruck.d = GetIstdruck()
                        Wend
                    EndIf
                    If ExitDrucktabelleThread.i <> #True
                        ;Hier Stelldruck speichern
                        Drucktabelle.w(IndexDrucktabelle.i) = Round(Stelldruck.d * 1000, #PB_Round_Nearest) * -1
                        Debug "StelldruckInt_____="+Str(Drucktabelle.w(IndexDrucktabelle.i))
                        IndexDrucktabelle.i = IndexDrucktabelle.i + 1
                        ;
                        Solldruck.d = Solldruck.d - SolldruckStufung.d
                    EndIf
                Wend
            EndIf
            ;
            If ExitDrucktabelleThread.i <> #True
                Druckfenster.d = SysParDruckgrenzeNegativ.d
                While Druckfenster.d <= SysParDruckgrenzePositiv.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    Drucktabelle.w(IndexDrucktabelle.i) = 0
                    Debug "StelldruckInt_____="+Str(Drucktabelle.w(IndexDrucktabelle.i))
                    IndexDrucktabelle.i = IndexDrucktabelle.i + 1
                Wend
            EndIf
            ;
            If ExitDrucktabelleThread.i <> #True
                SetDOut(#VakuumventilY8, 0)
                SolldruckStart.d = SysParDruckgrenzePositiv.d + SolldruckStufung.d
                Solldruck.d = SolldruckStart.d
                Stelldruck.d = StelldruckStart.d
                While Solldruck.d < SolldruckEnde.d
                    If ExitDrucktabelleThread.i = #True : Break : EndIf
                    SetSolldruck(Solldruck.d)
                    If Istdruck.d > Solldruck.d : RaiseError(#ERROR_IstdruckGroesserSolldruck) : EndIf
                    If ExitDrucktabelleThread.i <> #True
                        While Istdruck.d < Solldruck.d
                            If ExitDrucktabelleThread.i = #True : Break : EndIf
                            Stelldruck.d = Stelldruck.d + StelldruckStufung.d 
                            ;Debug "Stelldruck________="+StrD(Stelldruck.d)
                            If Stelldruck.d > StelldruckEnde.d : RaiseError(#ERROR_SolldruckNichtErreichbar) : EndIf 
                            SetStelldruck(Stelldruck.d)
                            ;Delay(#DelayDrucktabelleThread)                    
                            Istdruck.d = GetIstdruck()
                        Wend
                    EndIf
                    If ExitDrucktabelleThread.i <> #True
                        ;Hier Stelldruck speichern
                        Drucktabelle.w(IndexDrucktabelle.i) = Round(Stelldruck.d * 1000, #PB_Round_Nearest)
                        Debug "StelldruckInt_____="+Str(Drucktabelle.w(IndexDrucktabelle.i))
                        IndexDrucktabelle.i = IndexDrucktabelle.i + 1
                        ;
                        Solldruck.d = Solldruck.d + SolldruckStufung.d
                    EndIf
                Wend
            EndIf
        EndIf     
    EndIf    
    If ExitDrucktabelleThread.i <> #True
        ;
        ;Hier Drucktabelle an Basisplatine senden
        SysParAnzahlWerteDrucktabelle.d = IndexDrucktabelle.i
        SysParMinDruckDrucktabelle.d = SolldruckStart.d
        SysParMaxDruckDrucktabelle.d = SolldruckEnde.d
        SetSysPar(#RegSysParAnzahlWerteDrucktabelle, SysParAnzahlWerteDrucktabelle.d)
        SetSysPar(#RegSysParMinDruckDrucktabelle, SysParMinDruckDrucktabelle.d)
        SetSysPar(#RegSysParMaxDruckDrucktabelle, SysParMaxDruckDrucktabelle.d)
        ;
        SetControlVar(#RegCtrlVarRun, #RunSetDrkTab)
        ;
        For Index.i = 0 To SysParAnzahlWerteDrucktabelle.d - 1
            SetControlVar(#RegCtrlVarValue, Drucktabelle.w(Index.i))
            WaitControlVar(#RegCtrlVarValue, -32768)
            Debug "Index="+Str(Index.i)
        Next
        SetControlVar(#RegCtrlVarRun, #RunDoNothing)
        ;
        SysParDrucktabelleGueltig.d = #FlagDrucktabelleGueltig
        SetSysPar(#RegSysParDrucktabelleGueltig, SysParDrucktabelleGueltig.d)
    EndIf
    ;
    SetDOut(#PruefdruckventilY2, 0)
    SetDOut(#AbsperrventilY3, 0)
    SetDOut(#VakuumventilY8, 0)
    IstdruckRefresh.i = #True
EndProcedure


Gruß

Daffy
Wir sind LINUX
Widerstand ist zwecklos - Sie werden emuliert
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: KillThread

Beitrag von Thorium »

Das ProcedureReturn nicht funktioniert ist aber merkwürdig. Das dürfte ein Bug sein. Denn ein Rücksprung aus einem Thread ist durchaus möglich. Auf dem Stack liegt nach erstellen des Threads nämlich eine gültige Rücksprungadresse in die Kernel32.dll wo der Thread dann ordnungsgemäß beendet wird. Jedenfall wenn wir von Windows reden.

Ansonsten kannst du auch ExitThread_(ExitCode) unter Windows verwenden um den Thread sich selbst beenden zu lassen.

Zu den sporadischen Aussteigern: Hast du Threadsafe aktiviert? Das muss zwingend aktiviert werden, wenn du mit Strings in Threads arbeitest.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
Daffy0815
Beiträge: 390
Registriert: 15.06.2005 00:44
Wohnort: 65719 Hofheim
Kontaktdaten:

Re: KillThread

Beitrag von Daffy0815 »

@Thorium

Das ist gut möglich denn das mit dem "IstDruckRefresh.i" habe ich erst später eingebaut weil GetIstDruck auch vom Hauptprogramm (das ja im Hintergrund weiterläuft) aufgerufen wird. Und da hätte sich durchaus etwas "verheddern" können.
IstDruckRefresh.i sperrt nun die Abfrage im Hauptprogramm solange der Thread läuft.

Das mit dem "ExitThread_" ist schon mal ganz gut hat aber noch einen kleinen Haken.
So wie das jetzt geschieben ist, werden beim Abbruch des Threads immer alle Ventile geschlossen.
Würde man es mit dem ExitThread_ machen, so ginge das dann nur mit dem ExitCode und da müsste man wieder im Hauptprogramm eine Abfrage einbauen.

Wäre aber bestimmt einfacher als die jetzige Lösung!
Ideal wäre eigentlich "KillThread Goto ..."

Gruß

Daffy
Wir sind LINUX
Widerstand ist zwecklos - Sie werden emuliert
Antworten