Alaska-Sample myThread

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Benutzeravatar
mikehoffmann
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 133
Registriert: Mo, 21. Sep 2015 16:22
Hat sich bedankt: 1 Mal
Danksagung erhalten: 18 Mal

Re: Alaska-Sample myThread

Beitrag von mikehoffmann »

Habe noch etwas gutgemeinten Senf dazu. Hier isser.

Wie Tom schreibt, nehmt das Zeug für die Synchonisierung, was dafür gedacht ist. Das wäre die Signal Class. Da weiss das Betriebssystem (hoffentlich), dass Ihr Euch zur Ruhe legen wollt, bis was Aufregendes passiert. Außerdem hätte auch das Xbase++ Runtime-System dann Zeit für Hausaufgaben, wenn es diese Zeit nutzen möchte. Was in der Hilfe vom sofortigen Wiederanlauf nach Weckruf steht, kann nicht stimmen, aber dennoch geht es nicht besser als so.

Meine Forschungen haben ergeben, dass man mit Xbase++ O/S Threads recyceln kann und das ist überhaupt keine gute Idee. Geladene Gäste, also bewusst geladene Dlls und ungeladene Gäste, also Dlls, die die geladenen Gäste heimlich mitbringen, haben eine etwas andere Weltsicht. Bei denen gibt es so was Feines wie Thread Statics. Sone Dinger werden beim Neustart eines Threads frisch zubereitet für das werte Programm-Publikum. Geschieht diese frische Zubereitung nicht, könnte es zu Magenverstimmungen bei den werten und unwerten Gästen kommen. Wer mag schon an den alten Pommes seines Sitzplatzvorgängers weiterknabbern?

Deswegen laufen die Threads bei mir nach einem :Start() immer durch bis zur Ausgangstüre und werden gar nie nicht mit einem zweiten Start() recycelt. Und man darf auch nirgends eine Referenz darauf behalten, damit der wahre O/S-Thread die Hintertüre finden kann. Wann er sich wirklich vom Acker macht, das weiß man allerdings nicht.

Und am Schluss noch ein Quiz als abend- und tränenamphorenfüllende Alternative zum Corona-Ticker: Warum kommt dieses Programm nie so richtig in Schwung (dauerhaft 0% CPU) trotz Anwendung der empfohlenen Technik?

CLASS A
EXPORTED:
CLASS METHOD InitClass
ENDCLASS

CLASS METHOD A:InitClass
LOCAL s := Signal():New()
Thread():New():Start({||B(s)})
s:Wait()
RETURN self

CLASS B
EXPORTED:
CLASS METHOD InitClass
ENDCLASS

CLASS METHOD B:InitClass(s)
s:Signal()
RETURN self

FUNCTION Main
A()
RETURN NIL
ramses
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2513
Registriert: Mi, 28. Jul 2010 17:16
Hat sich bedankt: 12 Mal
Danksagung erhalten: 77 Mal

Re: Alaska-Sample myThread

Beitrag von ramses »

Wann er sich wirklich vom Acker macht, das weiß man allerdings nicht.
Hallo Mike

genau das ist das Problem !!
Dann bleiben die stehen bis der Speicher ausgeht und das Programm mit Fehler abbricht.
Fehler sind keine Option.
Deshalb Thread-Recycling und das Problem ist beseitigt.
Valar Morghulis

Gruss Carlo
Benutzeravatar
mikehoffmann
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 133
Registriert: Mo, 21. Sep 2015 16:22
Hat sich bedankt: 1 Mal
Danksagung erhalten: 18 Mal

Re: Alaska-Sample myThread

Beitrag von mikehoffmann »

Interessanter Schluss. Aber mein Quiz-Beispiel bleibt stehen und es bewegt sich gar nichts mehr. Keine CPU-Zeit, kein Speicherbedarf, Totenstille.

Ich ziehe folgenden Schluss aus meinen Beobachtungen bei der Beendigung eines Xbase-Threads: Der O/S-Thread darf sich vom Acker machen, wenn der Garbage Collector ihn als überflüssig identifiziert und alle ihm zugeordneten Xbase-Ressourcen freigegeben hat. Und genau dafür muss man sorgen. Je nach CPU-Last kann es bis zu diesem finalen Exitus etwas dauern, weil der Müllsammler der Anwendung den Zeitscheibenvortritt lässt.

Die Heisenbergsche Unschärferelation ("der gemessene Wert wird durch die Messung verändert") kommt bei den Threads immer ins Spiel, wenn man sie beobachtet und dafür eine Referenz auf sie hält. Dann lässt der Müllsammler die Delinquenten nämlich in Ruhe und sie sitzen weiter auf ihren Ressourcen und tun ... nichts.

Noch mal kurz zur Signal Class. Wenn man da den :Wait() ausführt, dann ist das vielleicht auch ein Hinweis an den GC: Schaff was, Du darfst die CPU jetzt nutzen. Noch eine Etage tiefer, ich nutze die O/S-Synchronisierungsobjekte, ist so ein Wait eine Übergabe des Threads an das O/S mit dem Hinweis: Nimm ihn, wenn Du ihn brauchen kannst, und mach was Gescheites damit. Oder warte halt.

Am Ende ist es wie in der Marlboro-Zigarettenwerbung: "You decide."
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Re: Alaska-Sample myThread

Beitrag von Jan »

Moin allerseits,

da hat sich ja am Wochenende noch eine nette Diskussion ergeben. Dafür erst mal Danke.

Ohne jetzt auf Eure Kommentare und Vorschläge einzugehen (das mach ich später in weiteren Antworten) laßt mich ein paar Gedanken und Beobachtungen zu dem Thema los werden.

1) Ich bin grundsätzlich offen für Lösungsvorschläge (fast) jeder Art. Solange sie zielführend sind für das Projekt - nicht alles was prinzipiell laufen würde wird da rein passen.

2) Ich hatte das Alaska-Sample genommen, weil das in der Online-Hilfe fertig stand. Aber es scheint halt eben nicht fertig zu sein. Denn so wie es da steht funktioniert das bei mir nicht. In der Diskussion hier gab es ja den Hinweis das man das Ergebnis daraus selber umsetzen muß. Darauf ist aber das Sample gar nicht vorbereitet. Wenn ich mir z. B. anschaue das :terminated als PROTECTED eingebaut ist, dann kann ich von außen das ja gar nicht abfragen - wie also soll ich das von außen nutzen können? OK, das mag auch meinem mangelnden Verständnis für Klassenprogrammierung geschuldet sein.

3) Was ich nicht verstehe ist: Warum klappt das nicht, selbst wenn ich das umbaue? Zur Erklärung: Das Projekt arbeitet relativ streng auf Tabpages. Jede Tabpage startet einen eigenen Thread für die Funktionalität, die darin ausgeführt werten soll. Es gibt Tabs, die haben einen Schließen-Button, da wird dann das oThread:quit drüber ausgeführt - was auch funktioniert. Es gibt aber auch einige, die sind immer mit irgend einer Funktion fest belegt. Und wenn ich die anspringe muß die dazugehörige Funktion automatisch ausgeführt werden. In seinem Thread halt. Und auf diesen Tabs habe ich das Problem. Wenn ich den Tab aufgerufen habe, auf einen anderen Tab wechsle, und wieder auf den Tab zurück gehe, dann muß dort ein neuer Thread gestartet werden. Oder der vorhandene Thread neu gestartet werden. Genau das funktioniert aber nicht. Weil :active immer auf .T. steht bei weiteren Aufrufen. und ich damit nicht auf :start komme. Das war ja ein Vorschlag von Carlo gewesen, der dann von Michael vehement abgelehnt wurde wegen irgendwelcher möglichen Probleme.

Eine Beobachtung zu 3), die ich inzwischen gemacht habe: Jeder Thread hat seine eigene Event-Schleife. Und sobald ich den Tab wechsle scheint der stehen zu bleiben. Also z. B.: Ich gehe auf Tab 2, der Thread dort wird gestartet. Ich gehe auf Tab 1, ab da scheint die Schleife in Tab 2 zu stehen. Ich habe das mit verschiedenen Wegen getestet. Z. B. eine PUBLIC genommen, deren Wert in der Schleife von Tab2 ausgewertet wird. Diese Auswertung findet aber nie statt, sobald ich den Tab verlasse.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12903
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 44 Mal

Re: Alaska-Sample myThread

Beitrag von AUGE_OHR »

hi,

wenn man 2 Objecte so "verknüpft" wäre eine

Code: Alles auswählen

CLASS ABC FROM XbüTabage, Thread
sinnvoll.
gruss by OHR
Jimmy
ramses
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2513
Registriert: Mi, 28. Jul 2010 17:16
Hat sich bedankt: 12 Mal
Danksagung erhalten: 77 Mal

Re: Alaska-Sample myThread

Beitrag von ramses »

@ Mike

Hallo Mike

die CPU-Last in der Anwendung von der ich Sprach ist nie 0% sondern je nach Tageszeit liegt diese nach Angabe des Taskmanager immer mindestens im hohen 1 stelligen Bereich. Bevor ich begonnen habe Threadrecycling zu benutzen hingen oft mehrere 100 beendete Threads bis es dann irgendwann mit absoluter Sicherheit zum Crash kam. Dies geschiet nun nicht mehr und das Programm läuft über Wochen ....

@ Jan

Hallo Jan

in deinem Pt. 3 :active des Threads bleibt solange .t bis du ::quit() ausführst oder der Code (Thread) zuende gelaufen ist. .....

Mit den Tabpages bzw. Tab-Wechsel Sorgen habe ich auch schon sehr viel Zeit aufgewendet und mich abgemüht.
Es ist zu lange her, da kann ich dir nicht mehr weiterhelfen. Ich hab die Tricks vergessen.

Meine/Usere Programmteile die diesen Code enthielten waren die ersten die wir ab 2010 auf HTML umgeschrieben hatten. Ich hatte dazu mehrere Web Gurus als Mitarbeiter und echt Super Tools fürs Formdesign usw.
Auch wenn es dir nicht weiterhilft: In der neuen HTML Fassung hatten wir auch nie mehr irgendwelche Probleme gehabt.

Ich frage mich ob Xbase für solche Aufwendigen Forms wie mehrseitige Tabpages überhaupt das richtige Tool ist
weil ja nur schon der Seitenaufbau der Tabs oft mehrere Sekunden benötigte bis alles gezeichnet war und das Programm überhaupt auf Eingaben reagierte ....
Valar Morghulis

Gruss Carlo
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Re: Alaska-Sample myThread

Beitrag von Jan »

Moin allerseits,

noch mal Danke für all die Ideen und Vorschläge. Manche davon waren eher akademischer Art, einer auch vollkommen abwegig da ich damit das komplette Projekt hätte umschreiben müssen. Manches wäre sicher sinnvoll, wenn das Projekt am Start stände, aber ich muß jetzt mit den historischen Gegebenheiten des Projektes klar kommen.

Nach viel rumtesten und probieren habe ich jetzt anscheinend wirklich eine Lösung hinbekommen. Die ist sicher für manchen hier zum Haareraufen. Aber egal, es scheint sauber und flott zu klappen. Und zwar läuft das über Signal(). Etwas, dessen Konzept mir vollkommen unverständlich war. Auf jeden Fall gibt es jetzt trotz wiederholter Aufrufe nur einen Thread - etwas was ich vorher nie hinbekommen hatte.

Was ich mache (auf das allernotwendigste zusammen geschrumpft):

Code: Alles auswählen

FUNCTION main
LOCAL i            := 0
PUBLIC oTab2Thread := NIL
PUBLIC oTab2Signal := Signal():new()

FOR i := 1 TO 10
    test()
NEXT

RETURN NIL

***

FUNCTION test

IF MEMVAR->oTab2Thread = NIL
  MEMVAR->oTab2Thread := Thread():new()

 ELSE
   MEMVAR->oTab2Signal:signal()
ENDIF

MEMVAR->oTab2Thread:start("Testfunktion")

RETURN NIL

***

FUNCTION testfunktion

LOCAL nEvent    := 0
LOCAL mp1       := NIL
LOCAL mp2       := NIL
LCOAL oEventXbp := NIL

DO WHILE .T.
   nEvent := AppEvent(@mp1, @mp2, @oEventXbp)
   IF MEMVAR->oTab2Signal:wait(1)
      MEMVAR->oTab2Thread:quit()
   ENDIF
   oEventXbp:handleEvent(nEvent, mp1, mp2)
ENDDO

RETURN NIL
Gibt es da noch Optionierungsmöglichkeiten? Ja, garantiert. Aber die müssen natürlich halbwegs zu den Gegebenheiten passen.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
ramses
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2513
Registriert: Mi, 28. Jul 2010 17:16
Hat sich bedankt: 12 Mal
Danksagung erhalten: 77 Mal

Re: Alaska-Sample myThread

Beitrag von ramses »

Hallo Jan
Jan hat geschrieben: Do, 21. Mai 2020 11:15 ....einer auch vollkommen abwegig da ich damit das komplette Projekt hätte umschreiben müssen. Manches wäre sicher sinnvoll, wenn das Projekt am Start stände, aber ich muß jetzt mit den historischen Gegebenheiten des Projektes klar kommen.
Ja, ich weiss. Entschuldige.
Wir mussten wirklich alles Umschreiben es war aber einfacher und schneller als zuerst gedacht.
Das einzige was mich damals dazu motiviert konnte war $$$$$$$$
Heute muss ich sagen "zum Glück" den das Projekt wird anno 2020 nun hauptsächlich auf Tablets und nur noch wenigen Windows Geräten (auch im Browser) genutzt. Genau so wie es der damalige "Entscheidungsträger" bereits 2008 vorhergesagt hatte ....

Wenn du deine Lösung gefunden hast ist das doch für dein Projekt perfekt.
Valar Morghulis

Gruss Carlo
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Re: Alaska-Sample myThread

Beitrag von Jan »

Carlo,

fühl Dich doch nicht gleich angegriffen! Erstens wollte ich damit nienmanden bloß stellen. Und außerdem warst gar nicht Du gemeint. Dein Gedanke wäre ja eine komplette Neuentwicklung im Bereich der GUI gewesen und war nur als Hinweis gedacht, wie man es machen kann mit der Tabverwaltung.

Was mich ein wenig ärgert ist das Alaska-Sample. Das wird da so hingestellt als ob das die Lösung wäre. Ist es aber nicht einmal ansatzweise. Das hat mich jetzt eine Menge Zeit gekostet (und Dich auch mit Deinen Verbesserungsvorschlägen dazu), was wir beide uns hätten sparen können wenn von vorneherein klar gewesen wäre, daß das nur rudimentär ist und so niemals funktioniert.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Re: Alaska-Sample myThread

Beitrag von Jan »

Nachtrag: Die Zeile

Code: Alles auswählen

   IF MEMVAR->oTab2Signal:wait(1)
in der Event-Schleife hält leider ab und an den ganzen Laden auf. Werte kleiner als 1 werden aber als 0 angesehen und sind damit noch kontraproduktiver.

Ich habe also noch eine PUBLIC eingeführt (auch wenn ich PUBLICS eigentlich wo immer möglich vermeiden möchte), die die Eventschleife nur dann den :wait aufrufen lässt, wenn es wirklich notwendig ist. Und damit klappt das jetzt sauber und auch sehr performant. Damit sieht das jetzt so aus:

Code: Alles auswählen

FUNCTION main
LOCAL i            := 0
PUBLIC oTab2Thread := NIL
PUBLIC oTab2Signal := Signal():new()
PUBLIC lTab2Signal := .F.

FOR i := 1 TO 10
    test()
NEXT

RETURN NIL

***

FUNCTION test

IF MEMVAR->oTab2Thread = NIL
  MEMVAR->oTab2Thread := Thread():new()

 ELSE
   MEMVAR->lTabSignal := .T.
   MEMVAR->oTab2Signal:signal()
ENDIF

MEMVAR->oTab2Thread:start("Testfunktion")

RETURN NIL

***

FUNCTION testfunktion

LOCAL nEvent    := 0
LOCAL mp1       := NIL
LOCAL mp2       := NIL
LOCAL oEventXbp := NIL

lTab2Signal := .T.

DO WHILE .T.
   nEvent := AppEvent(@mp1, @mp2, @oEventXbp)
   IF lTab2Signal = .T.
      IF MEMVAR->oTab2Signal:wait(1)
         MEMVAR->oTab2Thread:quit()
      ENDIF
   ENDIF
   oEventXbp:handleEvent(nEvent, mp1, mp2)
ENDDO

RETURN NIL
Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Antworten