![Confused :?](./images/smilies/confused.gif)
jan
Moderator: Moderatoren
Code: Alles auswählen
CLASS RunDog FROM Thread
PROTECTED:
VAR terminated
VAR aniAct
EXPORTED:
VAR workCounter
INLINE METHOD init
::Thread:init()
::terminated := .F.
::aniAct := 1010
RETURN self
INLINE METHOD stop
RETURN ( ::terminated := .T. )
METHOD execute, checkTermination
ENDCLASS
// Diese Methode wird im Thread ausgeführt
METHOD RunDog:execute
do while .not. ::terminated
oApp:setPointer(,::aniAct,XBPWINDOW_POINTERTYPE_ICON)
::aniAct += 10
if ::aniAct > 1060
::aniAct := 1010
endif
sleep( 15 )
::checkTermination()
enddo
RETURN self
// Diese Methode prüft ob der Thread abgebrochen werden muß
// und terminiert ihn gegebenenfalls
METHOD RunDog:checkTermination
IF ::terminated
::terminated := .F.
oApp:setPointer( , 0, XBPWINDOW_POINTERTYPE_SYSPOINTER )
::quit()
ENDIF
RETURN self
Code: Alles auswählen
oRunDog := RunDog():New()
oRunDog:start()
Code: Alles auswählen
oRunDog:stop()
ich habe alle Thread Objecte in ein Array aufgenommen.Jan hat geschrieben:Kann mir jemand sagen, wie ich einen bestimmten Thread aus einem anderen heraus beenden kann? Ich kann den mit dem Threadnamen identifizieren...
Code: Alles auswählen
#define XbeE_StopThread xbeP_User+100
PostAppEvent(XbeE_StopThread,,,myThreadObject)
Code: Alles auswählen
DO WHILE .NOT. lExit
nEvent := AppEvent( @mp1, @mp2, @oXbp, nTimeOut)
DO CASE
CASE nEvent == xbe_None // Timeout
EXIT
CASE nEvent == XbeE_StopThread
EXIT
Code: Alles auswählen
#TRANSLATE Thread() => MyThread()
Jetzt kommen die Vorlieben der einzelnen Programmierer zum Tragen.
Diese Aussage bezieht sich auf meine Programme - dort hat das bis heute sehr gut funktioniert. Aber auch Deiner Aussageauf meine Thread-Klasse mappe. Die enthält eine Liste aller aktiven Threads, sowie einen Link zum jeweiligen Fenster (kein Thread ohne eigenes Fenster).
kann ich zustimmen!Kein Fenster ohne eigenen Thread!
Genau da liegt die Idee der eigenen Thread-Programmierung: Etwas im Hintergrund erledigen ohne dass der Anwender etwas davon merkt und seine Arbeit dadurch nicht betroffen ist.Klaus Schuster hat geschrieben: ↑Mi, 14. Feb 2018 19:46 'kein Thread ohne Fenster' würde die Einsatzmöglichkeiten sehr einschränken. Threads können im Hintergrund E-Mails versenden, Berechnungen ausführen, Daten prüfen, usw. ohne ein eigenes Fenster zu haben.
Da bin ich gar nicht einverstanden.Klaus Schuster hat geschrieben: ↑Mi, 14. Feb 2018 19:46Ich neigen eher zum ungekehrten Schluss: Kein Fenster ohne eigenen Thread!
Modale Fenster sollten ohnehin in einem schlauen Programm nie oder nur ganz selten (Datensicherung usw.) vorkommen.Sonst müssten wir dann mal über Design sprechen.
zurück zu Deiner eigentlichen Frage:Klaus Schuster hat geschrieben: ↑Mi, 14. Feb 2018 18:34Ausgeführt wird der Thread, zeigt aber nichts an.
Ja, genau so macht das SinnJan hat geschrieben: ↑Do, 15. Feb 2018 8:15 Bei meinen Kunden lasse ich normalerweise nur Hintergrundprozesse als eigener Thread laufen. Wie die Abfrage nach neuen internen Nachrichten, die dann in einem Infofenster aufgeführt werden. Oder eine Videoüberwachung, wo jede Kamera in einem eigenen Thread läuft.
Allerdings habe ich eine eigene Software, die stark in Tabpages strukturiert ist. Und hier ist jede Tabpage ein eigener Thread, das habe ich vor ca. 10 Jahren einfach mal so umgesetzt bei der Umstellung von Clipper auf Xbase++. Ob ich das heute noch genau so stringent durchziehen würde kann ich nicht sagen. Manche der Tabpages stellen Auswertungen zusammen und geben die danach aus - da ist es gut, das die als Thread laufen, man kann währenddessen gut in anderen Tabpages weiter arbeiten. Andere geben nur aus oder dienen der Datenerfassung - ob da ein Thread sinnvoll ist mag zumindest diskussionswürdig sein.
Werden in einem Thread aber Dialoge geöffnet (modal oder wie auch immer), laufen die dann aber nicht in eigenen Threads. Ich wollte es dann doch auch nicht übertreiben.
Dass ein Thread Infos über seinen Fortschritt anzeigt, ist schon sinnvoll, ich lese z.B. große Dateien ein.Herbert hat geschrieben: ↑Do, 15. Feb 2018 7:45 Ein Thread läuft in sich geschlossen. Daher ist ein Thread, welcher GUI-Elemente verwendet, in der Regel nicht sinnvoll.
Oder der Thread verarbeitet etwas, was nur während der Lebzeit dieses Fensters gilt. Nur weiss der Thread nicht, wann der User dieses Fenbster schliesst.
Code: Alles auswählen
* __DateiImport wurde mit XppFD grob erstellt und dann nachgebessert
CLASS _DateiImport FROM __DateiImport
PROTECTED:
VAR oThread ********************************* diese Variable enthält den Verweis im Fenster auf den Importthread, siehe CLOSE !!!
...
EXPORTED:
METHOD init
METHOD create
METHOD CLOSE
METHOD DateiImportieren
...
ENDCLASS
*---------------------------------------------------------------------------
METHOD _DateiImport:close()
if ! ::oThread = NIL .and. ::oThread:active ******** das Fenster möchte schließen, aber Thread ist aktiv, was machen ???
*** in dem Beispiel erzwinge ich den Durchlauf .... man könnte das aber auch anders regeln
errbox("Einlesevorgang darf nicht abgebrochen werden !")
else
SetAppWindow( RootWindow())
::destroy()
endif
return self
*---------------------------------------------------------------------------
METHOD _DateiImport:DateiImportieren()
local cDatei
cDatei := ::sleDateiname:getData()
do case
case empty(cDatei)
errbox("Bitte Dateinamen eingeben oder mit ... ausw„hlen !")
SetAppFocus(::sleDateiname)
case empty(cDatei) .or. ! file( cDatei )
errbox("Datei: "+cDatei+" existiert nicht !"+CRLF+;
"Bitte Dateinamen eingeben oder mit ... ausw„hlen !")
SetAppFocus(::sleDateiname)
otherwise
**** alles disablen was nicht genutzt werden darf
::mleBericht:clear()
::pbDateiAuswahl:disable()
::pbImportStarten:disable()
::pbCancel:disable()
::pbBerichtDrucken:disable()
*** Thread anlegen, im Fenster Referenz hinterlegen und das Fenster selbst als Parameter an den Thread übergeben,
*** somit hat dieser Zugriff auf die GUI und VARS wenn er das braucht ...
::oThread := Thread():new()
::oThread:start("DoDateiImport", self , cDatei )
// Nach Ende des Threads setzt dieser die Controls wieder aktiv !
endcase
return self
Code: Alles auswählen
*---------------------------------------------------------------------------------------
function DoDateiImport(oXbp, cDatei) // cDatei ist Name der Textdatei
local nDB := NIL, ...
...
*** Zeienweise einlesen, dafür ist der TextReader() da
oTR := TextReader():new( cDatei )
if oTR:FError() <> 0
errbox("Fehler beim Öffnen der Datei !" + chr(13) + cDatei + chr(13) + oTR:ErrMsg())
return 1
endif
begin sequence
*** hier sollen die Daten rein, wichtig NEW öffnet die Datei in einem freien Selectbereich
USE (DatenVerzeichnis()+"xxxx") NEW
set index to (DatenVerzeichnis()+"xxxx")
*** ALIAS() ist für Menschen, PCs ziehen numerische SELECT() Werte vor, diese sind IMMER eindeutig !
nDB := Select()
...
nStart := seconds()
nFehler:= 0
nUnplausibel := 0
cTxt := ""
*** Infos an GUI übergeben !!!!
oXbp:sleFirmenName:SetData(cFirmenName) // wird in cDatei ermittelt
oXbp:sleFehlerAnzahl:SetData(alltrim(str(nFehler)))
*** MLE dient als Verlaufsanzeige, zunächst immer löschen, dann cTxt einfügen
*** ab einer gewissen länge wird gescrollt
oXbp:mleBericht:clear()
oXbp:mleBericht:insert(,cTxt)
*** gibt der GUI Zeit das anzuzeigen, sleep(0) kostet NICHTS, gibt aber Timeslice an nächsten Thread
sleep(0)
nZei := 0 // Textzeilenz„hler
nAnzByte := 0 // gelesene Byte aus Quelldatei
nAnz := 1 // hier wird mit einem angefangen, damit Anzeige besser aussieht
// dem Einlesen zu tun !
cTxt += dtoc(date())+" "+time()+" - "+alltrim(str(nAnz))+" Datensätze ..."+CRLF
oXbp:mleBericht:clear()
oXbp:mleBericht:insert(,cTxt)
nAnz := 0 // Eingelesen ist noch keiner, daher auf 0 setzen
nAnzByte := 0 // gelesene Byte
*** immer schön über die Anzahl der neuen Datensätze informieren
oXbp:sleSatzAnzahl:SetData(alltrim(str(nAnz)))
do while .t.
*** hier könnte man auch einen Abbruch einbauen !!!
if oXbp:Abbruch
msgbox( "von Benutzer abgebrochen" )
BREAK
endif
sleep(0)
*** ich nehme meine Textreader Klasse
cZei := oTR:GetLine()
nZei++
nAnzByte += len(cZei)+oTR:LenCrLf()
cSchl := upper(alltrim(left(cZei,10)))
*** Zeile einlesen, aufteilen speichern etc.
*** Anzahl anzeigen
nAnz++
oXbp:sleSatzAnzahl:SetData(alltrim(str(nAnz)))
if oTR:EOF()
exit
endif
enddo
nDauer := seconds() - nStart
beep()
cTxt += dtoc(date())+" "+time()+" - Es wurden "+if(nFehler=0,"keine",alltrim(transform(nFehler,"999,999,999,999")))+;
" Fehler gemeldet."+CRLF+;
dtoc(date())+" "+time()+" - "+alltrim(transform(nAnz,"999,999,999,999"))+;
" Datensätze wurden eingelesen (Dauer: "+alltrim(transform(nDauer/60,"9,999"))+" min)."+CRLF+;
...
*** Status ausgeben
oXbp:mleBericht:clear()
oXbp:mleBericht:insert(,cTxt)
end sequence
endif
oTR:destroy()
if (nDB)->(used())
(nDB)->(dbcloseArea()) // sicher ist sicher
oXbp:pbDateiAuswahl:enable()
oXbp:pbImportStarten:enable()
oXbp:pbCancel:enable()
oXbp:pbBerichtDrucken:enable()
endif
return nFehler