PUBLIC Variabeln, DBF und Thread´s syncronisieren ?

Eigentlich ist mir die Frage peinlich, aber es kann sonst niemand helfen ... :)

Moderator: Moderatoren

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

PUBLIC Variabeln, DBF und Thread´s syncronisieren ?

Beitrag von AUGE_OHR »

hi,

mal angenommen ich habe Threads "verschachtelt" und PUBLIC Variabeln.

Code: Alles auswählen

Thread1
PUBLIC a := "a"
...
starte -> Thread2
              PUBLIC b := "b"
              ? a
              ? b
              ...
              starte -> Thread3
                            PUBLIC c := "c"
                            ? a,b,c
...
? a
? b
? c
es laufen also 3 Thread´s.
Frage: wie komme ich vom Thread1 an das "Ergebniss" von Thread3 ?

das Beispiel verwendet hierbei nur eine vereinfachte Struktur. Es sind
aber völlig getrennte Module und durch die Threads laufen die DBF´s ja
in verschiedenen WorkspaceArea´s ...

Frage: wie bekomme ich z.b. die RECNO() von einem anderen Thread
raus damit ich z.b. was syncronisieren kann ?

gruss by OHR
Jimmy
notloesung
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 194
Registriert: Fr, 24. Feb 2006 8:09
Kontaktdaten:

Beitrag von notloesung »

Hallo Jimmy,

wir nutzten im diesem Zusammenhang bisher das alt bewährte Prinzip: Low-Level-Funktionen. Bisher läuft es überall problemlos.

Ich möchte aber nicht bestreiten dass es hierfür sicherlich eine "elegantere" Lösung gibt( :?: ).

Gruß,
Notloesung
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,

angenommen es wäre ein GUI-Programm und Thread 1 befinde sich in seiner Eventloop, würde ich das mit benutzerdefinierten Events lösen. Also z.B. in Thread 3, nachdem PUBLIC c geändert wurde,

PostAppEvent( xbe_C_changed, c, NIL, SetAppWindow() ) aufrufen.

Andererseits brauchst du dann die PUBLICs eventuell gar nicht mehr, weil alle interessierenden Werte über den Eventmechanismus an den Hauptthread gesendet werden können.

Viele Grüße,
Günter
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9345
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 100 Mal
Danksagung erhalten: 359 Mal
Kontaktdaten:

Beitrag von Tom »

Mustopf? Jedenfalls verstehe ich das Problem nicht; PUBLICs sind applikationsweit sichtbar, also auch dann, wenn sie in Threads manipuliert werden. Die "Ergebnisse" sind also jederzeit in allen Threads sichtbar - darin besteht auch gleichzeitig eine Gefahr, wenn man derlei macht. Eleganter wäre sicher der von Günter vorgeschlagene Weg. Die Threads könnten aber auch die Cargo-Instanzen ihrer Objekte besetzen, und die wiederum könnte man abfragen, weil die Thread-Objekte ja im Mainthread entstehen. Beispielsweise.
Herzlich,
Tom
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

Beitrag von AUGE_OHR »

hi,
Tom hat geschrieben: PUBLICs sind applikationsweit sichtbar, also auch dann, wenn sie in
Threads manipuliert werden. Die "Ergebnisse" sind also jederzeit in allen
Threads sichtbar - darin besteht auch gleichzeitig eine Gefahr, wenn
man derlei macht.
nun ja, auch wenn ich das unter Newbie eingestellt habe, hat das Ding
einen Harken : Die PUBLIC sind eben NICHT "überall sichtbar" wenn man
so "verschachtelt" Thread´s benutzt.

Klar muss ich noch eine sample bauen um das zu beweisen, aber ich
bin der Meinung das Problem schon mal bemerkt zu haben (Errorsys)
und hab dann die PUBLIC auf PRIVAT umgestellt und dann ging es ...
Tom hat geschrieben: Eleganter wäre sicher der von Günter vorgeschlagene Weg. Die Threads könnten aber auch die Cargo-Instanzen ihrer Objekte besetzen, und die wiederum könnte man abfragen, weil die Thread-Objekte ja im Mainthread entstehen. Beispielsweise.
das mit dem :Cargo für Thread´s hab ich noch nicht ganz Begriffen,
könnte mir jemand ein Beispiel geben.

gruss by OHR
Jimmy
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

Beitrag von AUGE_OHR »

hi,

hier nun ein Sample :

Code: Alles auswählen

PROCEDURE Main
LOCAL oThread1 := Thread():new()
PUBLIC a := "Hallo"

    CLS
    ? a
    oThread1:start( "DOThread2" )

*1
    ? a
    ? b
    ? c

    WAIT

RETURN

PROCEDURE DOThread2
LOCAL oThread2 := Thread():new()
PUBLIC b := " du"

    ? a+b
    oThread2:start( "DOThread3" )

RETURN

PROCEDURE DOThread3
LOCAL oThread3 := Thread():new()
PUBLIC c := " da"

   ? a+b+c
   oThread3:start( "DOThread4" )

RETURN

PROCEDURE DOThread4

   ? a+b+c+" aus 4"

   SLEEP(1000)

RETURN

*
* eof
*
gruss by OHR
Jimmy
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16502
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Jimmy,
Dein Sample knallt auf meinem PC, da in der Main mit ? b eine Variable ausgegeben werden soll, die es noch nicht gibt! Synchronistaionsproblem! Wenn ich dann sage "Wiederholen", dann geht es...
Wenn Du einen kurzen Sleep() einbauen würdest, würde es klappen...
Hängt aber natürlich von der Geschwindigkeit des Rechners ab...

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender 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

Beitrag von AUGE_OHR »

hi,
Martin Altmann hat geschrieben: Hallo Jimmy,
Dein Sample knallt auf meinem PC, da in der Main mit ? b eine Variable ausgegeben werden soll, die es noch nicht gibt! Synchronistaionsproblem! Wenn ich dann sage "Wiederholen", dann geht es...
Wenn Du einen kurzen Sleep() einbauen würdest, würde es klappen...
Hängt aber natürlich von der Geschwindigkeit des Rechners ab...
hm SLEEP() ... bräuchte ich vielleicht auch mal ...

damit hast die "Lösung" auch schon verraten :)
klar hat das was mit dem sycronisieren zu tun ... und da liegt mein
Problem mit SLEEP(je_nach_Rechner) und ich suche etwas "anderes"
was eleganter auf "je_nach_Rechner" reagiert.
Tom hat geschrieben: PUBLICs sind applikationsweit sichtbar
natürlich hat Tom grundsätzlich recht !

das Problem ist aber weniger die PUBLIC Variabeln selbst sondern in der
"Verschachtelung" der Threads und deren "Verzögerrung" deren Existenz
zu dem Zeitpunkt wo ich die (ohne Test) abfrage.

auch steht PUBLIC eher symbolisch für die Informationen aus den activeX
Threads und sollen andeuten das es in allen Stufen der "Verschachtung"
zu erreichen ist.

ich hätte die Überschrift des Thread von hinten anfangen sollen :
"verschachtlung syncronisieren Thread´s DBF Variabeln PUBLIC ?"
Günter Beyes hat geschrieben: PostAppEvent( xbe_C_changed, c, NIL, SetAppWindow() )
hm ... Usedef Event ... ja sowas in der Art werde ich mal versuchen.

danke allen,
gruss by OHR
Jimmy
Benutzeravatar
andreas
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1902
Registriert: Mi, 28. Sep 2005 10:53
Wohnort: Osnabrück
Hat sich bedankt: 4 Mal
Kontaktdaten:

Beitrag von andreas »

Hallo Jimmy,

du könntest zum Warten auf die Antwort eines anderen Threads ThreadWait benutzen. Das musst dann evtl. in jedem deinen Threads einbauen.
Evtl. auf die Existenz der Variablen mit IsMemvar prüfen, bevor du auf diese zugreifst.
Gruß,

Andreas
VIP der XUG Osnabrück
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

Beitrag von AUGE_OHR »

hi,

erstmal Danke für eure Vorschläge. Nun hab ich ein externes Modul
gebaut wo ich ein Static Array benutze und die Elemente per Funktion
abfrage welchen Status die haben/hatten.

leider hab ich immer noch das Problem mit dem Syncronisieren, den
es ist Möglich das im selben Moment wo ich abfrage ein anderer Thread
gerade dabei ist zu schreiben ... ich weiss aber nicht wann er schreibt
und wann ich dann lesen darf ... ich muss also 2x lesen um sicher zu sein
das zu dem Zeitpunkt der Status noch gilt (was sich aber Bruchteile von
Sekunden später ändern könnte ... )

Bei dem von Andreas angesprochene ThreadWait() weiss ich nicht genau
wie ich das verwenden soll ... hat jemand ein Beispiel ausser das Beispiel
C:\ALASKA\XPPW32\SOURCE\samples\basics\THREAD\Worms.prg ?

gruss by OHR
Jimmy
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16502
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Jimmy,
schau Dir mal das Coffee-Beispiel im selben Verzeichnis an...

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender 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

Beitrag von AUGE_OHR »

hi,
Martin Altmann hat geschrieben: schau Dir mal das Coffee-Beispiel im selben Verzeichnis an...
hm ... über eine "Zeit-Scheibe" ... das bringt mich auf eine Idee.

ok mal ausprobieren, danke.

gruss by OHR
Jimmy
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,

die Synchronisation beim Variablenzugriff könntest du über eine globale Klasse mit SYNC METHODs in den Griff bekommen (siehe unten).

Wenn der Erzeugerthread die Werte schneller ändert, als du sie im Verbraucherthread verarbeiten kannst, hilft das aber wohl auch nicht viel :(

Code: Alles auswählen

Anwendung:
SyncVars():VarPut( "Var1", value )
SyncVars():VarGet( "Var1" )

CLASS SyncVars

PROTECTED:

CLASS VAR var1

EXPORTED:

INLINE SYNC CLASS METHOD VarPut( cVarName, value )
::&cVarName := value
return ::&cVarName

INLINE SYNC CLASS METHOD VarGet( cVarName )
return ::&cVarName

INLINE METHOD Init
  MsgBox("SyncVar():New() ist verboten!")
RETURN self

INLINE SYNC CLASS METHOD InitClass
  ::var1 := 0
RETURN self

ENDCLASS

Viele Grüße
Günter
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

Beitrag von AUGE_OHR »

hi,
Günter Beyes hat geschrieben: die Synchronisation beim Variablenzugriff könntest du über eine globale Klasse mit SYNC METHODs in den Griff bekommen (siehe unten).
Mit SYNC METHOD hab ich noch nicht gearbeitet. Hab mal im Help File
gelesen und das scheint wohl die Lösung meines Problems zu sein.

Die Beispiele von Alaska verwirren eher, wie deren Sample No. 1 welches
jedesmal ein anderes Ergebniss liefert, als das die helfen.
Xbase+ Help file hat geschrieben: SYNC Methoden, die in unterschiedlichen Threads zur Ausführung kommen, werden aus der Sicht des Objekts (self) immer nacheinander abgearbeitet.
hm ... also eine Art Queue wo sich die Threads "einreihen" ?

wenn sich immer alle Threads in der Queue "anstellen", warum bekommt
man bei dem Alaska Sample No. 1 unterschiedliche Ergebnisse (meistens
bei 4st Thread und die anderen 1-3 sich meistens 0/NIL )

Nun hab ich dein Beispiel mit dem Sample No. 1 kombiniert :

Code: Alles auswählen

PROCEDURE Main
LOCAL i, aT[4]
LOCAL TimeOn := SECONDS()
LOCAL TimeDiff

   CLS
   FOR i:=1 TO 4
      aT[i] := Thread():new()       // Threads erzeugen
      aT[i]:start( "Test" ) // und starten
*      SLEEP(1)
   NEXT

   ThreadWait( aT )                 // Warten bis alle fertig sind ?
   ? TimeDiff := SECONDS()-TimeOn
   WAIT

RETURN                              // Threads beendet sind

** Code läuft in 4 Threads gleichzeitig ab
PROCEDURE Test()
LOCAL i, nRow, nCol, j
LOCAL x,cString

   nRow := ThreadID() + 10
   nCol := 0
                                    // Werte aus der
   FOR i:=1 TO 10000                // Queue lesen
      FOR j := 1 TO 4

/*
         so geht es nicht weil zwischen dem "lesen" und dem "schreiben"
         schon eine anderer Thread den dann "alten" Wert "lesen" könnte

         cString := "Var"+LTRIM(STR(J))
         x := SyncVars():VarGet(  cString   )
         DispOutAt( nRow, nCol+20, SyncVars():VarPut( cString, x+1 ) )
*/
         //
         // bringt nichts ...
         //
*         SLEEP(1)
         //
         // "lesen" und "schreiben" in einem "Rutsch"
         //
         DispOutAt( nRow, nCol+20, SyncVars():VarPut(  ;
                        ("Var"+LTRIM(STR(j)))         ,;
                        SyncVars():VarGet(             ;
                        ("Var"+LTRIM(STR(j)))  )+1     ))

      NEXT
   NEXT
RETURN

CLASS SyncVars

PROTECTED:

CLASS VAR var1
CLASS VAR var2
CLASS VAR var3
CLASS VAR var4

EXPORTED:

INLINE SYNC CLASS METHOD VarPut( cVarName, value )
::&cVarName := value
return ::&cVarName

INLINE SYNC CLASS METHOD VarGet( cVarName )
return ::&cVarName

INLINE METHOD Init
  MsgBox("SyncVar():New() ist verboten!")
RETURN self

INLINE SYNC CLASS METHOD InitClass
  ::var1 := 0
  ::var2 := 0
  ::var3 := 0
  ::var4 := 0
RETURN self

ENDCLASS
soweit sogut, alle 4 Thread scheinen zu laufen. Nur das Ergebniss verwirrt
mich schon wieder weil immer nur "einer" 40000 und immer ein "anderer"
oder auch mal keiner 40000:
39946
39979
40000
39994
Frage :
a.) warum sind die Ergebnisse verschieden ?
b.) wie bekomme ich alle immer auf 40000 (= 4 x 10000) ?

gruss by OHR
Jimmy
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,
a.) warum sind die Ergebnisse verschieden ?
b.) wie bekomme ich alle immer auf 40000 (= 4 x 10000) ?

Das täuscht, alle Variablen erreichen immer 40000 !

Code: Alles auswählen

ThreadWaitAll( aT )  // Warten bis alle fertig sind
		
? TimeDiff := SECONDS()-TimeOn
   
// Den Endstand anzeigen
FOR i:=1 TO 4
   ? SyncVars():VarGet( "Var"+LTRIM(STR(i)) )
NEXT

WAIT
Nur die Wege dorthin sind unterschiedlich. Hier siehst du, welchen Maximalwert jeder Thread bei den vier Variablen jeweils erreicht. Die 40000 ist jedenfalls viermal dabei. Glückspiel pur, wenn auch ohne Hauptgewinn :D

- Günter

Code: Alles auswählen

PROCEDURE Test()
LOCAL i, nRow, nCol, j
LOCAL x,cString
LOCAL cThread := "T"+ltrim(str(ThreadId()))
LOCAL cVar

   nRow := 15
   nCol := (ThreadID()-2) * 10

   DispOutAt( nRow, nCol, padl( cThread, 10 ) ) 

   FOR i:=1 TO 10 // 10000 
      FOR j := 1 TO 4
         cVar := "Var" + ltrim(str(j))

         DispOutAt( nRow+j, 0, cVar ) 
		 
         // "lesen" und "schreiben" in einem "Rutsch"

         DispOutAt( nRow+j, nCol, SyncVars():VarPut( cVar,;
                         SyncVars():VarGet( cVar ) + 1 ) ) 

      NEXT
   NEXT

RETURN
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

Beitrag von AUGE_OHR »

hi,
Günter Beyes hat geschrieben: Das täuscht, alle Variablen erreichen immer 40000 !

Code: Alles auswählen

ThreadWaitAll( aT )  // Warten bis alle fertig sind
? TimeDiff := SECONDS()-TimeOn
// Den Endstand anzeigen
FOR i:=1 TO 4
   ? SyncVars():VarGet( "Var"+LTRIM(STR(i)) )
NEXT
WAIT
Die 40000 ist jedenfalls viermal dabei. Glückspiel pur, wenn auch ohne Hauptgewinn :D
hm ... ja ... aber :(
4,81
39975
39972
39974
39971
Press any key to continue...

T3 T4 T5 T6
Var1 39990 39971 40000 39986
Var2 39988 39969 39998 39984
Var3 39989 39971 40000 39985
Var4 39986 39970 39997 39983
hab dein Code übernommen mit (4x)10000 Schleifen.

könnte es sein das evtl. meine Hardware oder andere Software (NAV)
dafür sorgen das ich nicht die 4x 40000 bekomme ?

gruss by OHR
Jimmy

Code: Alles auswählen

PROCEDURE Main
LOCAL i, aT[4]
LOCAL TimeOn := SECONDS()
LOCAL TimeDiff

   CLS
   FOR i:=1 TO 4
      aT[i] := Thread():new()       // Threads erzeugen
      aT[i]:start( "Test" ) // und starten
*      SLEEP(1)
   NEXT

   ThreadWait( aT )                 // Warten bis alle
   ? TimeDiff := SECONDS()-TimeOn

   // Den Endstand anzeigen 
   FOR i:=1 TO 4 
      ? SyncVars():VarGet( "Var"+LTRIM(STR(i)) ) 
   NEXT 
   WAIT

RETURN                              // Threads beendet sind

** Code läuft in 4 Threads gleichzeitig ab
PROCEDURE Test() 
LOCAL i, nRow, nCol, j 
LOCAL x,cString 
LOCAL cThread := "T"+ltrim(str(ThreadId())) 
LOCAL cVar 

   nRow := 15 
   nCol := (ThreadID()-2) * 10 
   DispOutAt( nRow, nCol, padl( cThread, 10 ) ) 
   FOR i:=1 TO 10000 
      FOR j := 1 TO 4 
         cVar := "Var" + ltrim(str(j)) 
         DispOutAt( nRow+j, 0, cVar ) 
         // "lesen" und "schreiben" in einem "Rutsch" 
         DispOutAt( nRow+j, nCol, SyncVars():VarPut( cVar,; 
                         SyncVars():VarGet( cVar ) + 1 ) ) 
      NEXT 
   NEXT 

RETURN
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,
4,81

39975
39972
39974
39971
Press any key to continue...

T3 T4 T5 T6
Var1 39990 39971 40000 39986
Var2 39988 39969 39998 39984
Var3 39989 39971 40000 39985
Var4 39986 39970 39997 39983
kurios...

Mir fällt auf, dass du offenbar ThreadWait( aT ) statt ThreadWaitAll( aT ) verwendest. ThreadWait() beendet aber den Wartezustand, wenn irgendein Thread aus aT fertig ist.

Wenn das nichts bringt, fällt mir nur noch ein, den Code mal auf anderer Hardware zu testen (z.B. auf einer lahmen Ente wie dieser hier :) -- AMD Sempron 2800+, 2GHz ) und auch die unterschiedlichen Xbase++ - Versionen und Hotfixes durchzuprobieren.


Viele Grüße,
Günter
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hallo,

das mit den Threads kann einem wirklich verwirren, da man davon ausgeht, dass die Reihenfolge irgendwie bekannt ist. Bei einem Vortrag zu dem Thema (damals für VisualObjects, das spielt aber keine Rolle) wurde aber bewiesen, dass die Reihenfolge der Abarbeitung und somit die Geschwindigkeit nicht voraussehbar ist (auch wenn oft der erste Thread zuerst fertig ist.). Wenn du nun das Programm beendest sobald einer der Threads fertig ist, erhälst du nie 4x40000 in den Variablen.

Daher sollte man ja im Hauptthread auch ein ...
DO WHILE .NOT. oThreadB:synchronize(1)
<Programm Code>
ENDDO

/* oder */

oThreadB:synchronize(0)
einbauen (habe ich mal in der Doku geklaut ;-) )

Dass jeder Thread seine Publics selbst erstellt führt dann zum Problem, wenn ein Thread beim Ausgeben ankommt, bevor alle bei der Zuweisung waren. Die Initialisierung sollte also vorher erfolgen.

Threads als solches sind schon ein verzwicktes Thema, wenn man diese häufig syncronisieren muss, sollte man sich überlegen ob es überhaupt sinnvoll ist sie zu verwenden, da syncronisieren immer erhebliche Zeit kostet.
Gruß
Hubert
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

Beitrag von AUGE_OHR »

hi,
Günter Beyes hat geschrieben: Mir fällt auf, dass du offenbar ThreadWait( aT ) statt ThreadWaitAll( aT ) verwendest. ThreadWait() beendet aber den Wartezustand, wenn irgendein Thread aus aT fertig ist.
Oh ja, du hast recht. Also schnell ausgewechselt und (voller Hoffung)
gestartet ...
4,59
39987
39987
39995
39992
Press any key to continue...

T3 T4 T5 T6
39933 39813 39926 39987
39937 39813 39926 39987
39944 39821 39934 39995
39941 39820 39930 39992
es dauert ein bisschen länger als vorher, aber nix mit 40000 ...
Günter Beyes hat geschrieben: Wenn das nichts bringt, fällt mir nur noch ein, den Code mal auf anderer Hardware zu testen (z.B. auf einer lahmen Ente wie dieser hier :) -- AMD Sempron 2800+, 2GHz ) und auch die unterschiedlichen Xbase++ - Versionen und Hotfixes durchzuprobieren.
ja auf den "Intel" kommt auf jeden Fall immer sowas raus ... (alle Hotfixe
im selben Verzeichniss)
Hubert hat geschrieben: bewiesen, dass die Reihenfolge der Abarbeitung und somit die Geschwindigkeit nicht voraussehbar ist ...
hm ... Aber nach dem ThreadWaitALL( aT ) müsste doch auch der 1st.
Thread weitermachen bis 40000 ... ?
Hubert hat geschrieben:

Code: Alles auswählen

DO WHILE .NOT. oThreadB:synchronize(1) 
<Programm Code> 
ENDDO 
/* oder */ 
oThreadB:synchronize(0)
gesehen ... aber noch nicht kapiert wie ich das in das Demo einbauen
soll ?

soweit wie wir nun damit sind fragt sich ob ich das ganze mal zu Alaska
schicken sollte ?

gruss by OHR
Jimmy
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hallo Jimmy,

ich muss zugeben, dass ich das Problem nicht richtig verstehe.

Die zu niedrig angezeigten Werte könnte ich mir mit 'Optimierungen des Grafiktreibers' in Bezug auf VIO / CRT Fenster vorstellen.
Eventuell werden die offenen Threads schneller geschlossen wie DispOutAt anzeigen kann. Übrigens ist die IO-Anzeige sehr langsam im Vergleich zum Prozessor.

Aber warum soviele Abhängigkeiten ?

Threads können nur 'sehr gut' arbeiten, wenn man die Abhängigkeiten durch Programmlogik verhindert. Wo es nicht geht gibt es signale (siehe Kaffeemaschine) oder Syncronisation. Ob aber ein ActiveX Objekt sich auch so syncronisieren läßt ist eine ganz andere Frage.

Aus meiner Sicht muss auch darauf geachtet werden, dass kein Thread automatisch beendet wird, bevor er selbst aufgeräumt hat eventuell in der atEnd() Methode. In der Main methode dann das ThreadWaitAll(0) um zu verhindern dass die Threads automatisch beendet werden. Dass das alles automatisch geht bezweifle ich eher.

Übel wird es dann allerdings wenn ein Thread nicht mehr beendet werden kann ... :?
Gruß
Hubert
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,

probier mal, in den :atEnd() -Slot jedes Threads folgenden Code einzubauen:

Code: Alles auswählen

aT[i]:atEnd := ;
  {||MsgBox("Thread " + var2char(ThreadID()) + " beendet" + chr(13) + ;
                  SyncVars():showAll() ) }

und in der SyncVars-Klasse:

INLINE SYNC CLASS METHOD ShowAll()

RETURN "Var1 " + var2char( ::Var1 ) + chr(13) + ;
       "Var2 " + var2char( ::Var2 ) + chr(13) + ;
       "Var3 " + var2char( ::Var3 ) + chr(13) + ;
       "Var4 " + var2char( ::Var4 )
Dies würde eventuelle Verfälschungen durch DispOutAt() ausschließen.
Bei dem Thread, der als letzter fertig ist, sollten alle vier Variablen schließlich und endlich den Wert 40000 haben (hier ist das jedenfalls so). <stirnrunzel>

Zu oThread:synchronize(0) --

ThreadWaitAll( aT ) und aeval(aT, {|o|o:synchronize(0)} ) müssten nach meiner Meinung gleichbedeutend sein.

Ich denke, wenn es auf deinem Entwicklungssystem partout nicht hinzukriegen ist, du aber bei dir doch noch ein anderes System finden kannst, wo es wie erwartet funktioniert, dürfte das auch für Alaska interessant sein. Eventuell könntest du mal sämtliche Hotfixes rausnehmen (ich verwende hier momentan keine).

Günter
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

Beitrag von AUGE_OHR »

hi,
Günter Beyes hat geschrieben: probier mal, in den :atEnd() -Slot jedes Threads folgenden Code einzubauen:

Code: Alles auswählen

aT[i]:atEnd := ;
  {||MsgBox("Thread " + var2char(ThreadID()) + " beendet" + chr(13) + ;
                  SyncVars():showAll() ) }
und in der SyncVars-Klasse:

INLINE SYNC CLASS METHOD ShowAll()

RETURN "Var1 " + var2char( ::Var1 ) + chr(13) + ;
       "Var2 " + var2char( ::Var2 ) + chr(13) + ;
       "Var3 " + var2char( ::Var3 ) + chr(13) + ;
       "Var4 " + var2char( ::Var4 )
Dies würde eventuelle Verfälschungen durch DispOutAt() ausschließen.
Bei dem Thread, der als letzter fertig ist, sollten alle vier Variablen schließlich und endlich den Wert 40000 haben (hier ist das jedenfalls so). <stirnrunzel>
tja was soll ich sagen : Nein auch hier bekomme ich nirgenswo 4x40000
angezeigt :(

aber durch die MSGBOX() sehe ich zumindest das die Thread auch nicht
in ihrer Reihenfolge wie die geöffnet wurde, auch geschlossen wird, wie
Hubert ja schon erwähnte. (wusste ich vorher nicht)

nun muss ich das ganze wohl nochmal auf anderen PC´s ausprobieren
ob ich da ebenfalls nicht 4x 40000 bekomme ... aber wieso geht es den
bei dir ...
hubert hat geschrieben: Ob aber ein ActiveX Objekt sich auch so syncronisieren läßt ist eine ganz andere Frage.
der Mediaplayer sendet alle möglichen Events wenn er einen bestimmten
Status erreicht hat. Einige Methodes des activeX sind aber nur in einem
bestimmten Status (Bereich) erreichbar. Nur wenn also bestimmte Status
meldungen "zusammen passen" (4x40000) kann man die Methode auf-
rufen.
Ich habe bislang per SLEEP() solange rumprobiert bis ich ein timeing auf
meinem PC hatte. das geht auch über einen weiten Bereich (800Mhz-
3.0GHz) aber z.b. ein einem 500Mhz P2 stimmt das timeing nicht ...
hubert hat geschrieben: Wo es nicht geht gibt es signale (siehe Kaffeemaschine) oder Syncronisation
Ja die Coffeemachine hatte ich als "Zeitscheibe" auch schon im Blick bis
mich Günter dann auf die SYNC METHODs aufmerksam machte.

Wenn ich das richtig verstehe soll dann das "SYNC Attribut" verhindern
das ein anderer Thread die Methode gleichzeitig ausführen kann ?!

Wenn ich das nun weiterhin richtig verstehe "reiht" sich der andere
Thread nun in die "Warteschlange" ein statt einfach vorbei-zu-laufen,
oder ?!

genau diesen "Warteschlangen" Effekt brauche ich aber. Ich könnte dann
alle SLEEP() ersetzten was das Ansprechverhalten der Application deutlich
steigen sollte. Dann brauche ich hoffendlich auch keine timeout Routinen
mehr.

gruss by OHR
Jimmy
Nachtrag : nun hab ich einen 850Mhz P3 unter W98 mal laufen lassen und
bemerkt je früher die anderen Thread fertig sind (teilweise bei 28000) um
so grösser wird die Wahrscheinlichkeit im letzten Thread 4x40000 zu
bekommen. 1x hab ich es geschafft, aber seit dem nicht mehr ...
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jimmy,
der Mediaplayer sendet alle möglichen Events wenn er einen bestimmten Status erreicht hat. Einige Methodes des activeX sind aber nur in einem bestimmten Status (Bereich) erreichbar. Nur wenn also bestimmte Statusmeldungen "zusammen passen" (4x40000) kann man die Methode aufrufen.
Ich muss gestehen, dass mir die Architektur deines Programms unklar ist. Müssen denn diese Events im Hauptthread der Anwendung verarbeitet werden? Wäre es nicht denkbar, diese Events in der Eventloop des Threads zu verarbeiten, der sie erzeugt (wenn er denn eine besitzt)? Oder laufen vier Mediaplayer parallel, die aus einem bestimmten Grund miteinander synchronisiert werden müssen?
Wenn ich das richtig verstehe soll dann das "SYNC Attribut" verhindern das ein anderer Thread die Methode gleichzeitig ausführen kann ?!
Ja, das ist so. Noch darüber hinausgehend meine ich, es wird verhindert, dass ein anderer Thread gleichzeitig (besser gesagt zeitlich überlappend) irgend eine SYNC METHOD derselben Klasse ausführen kann.
Wenn ich das nun weiterhin richtig verstehe "reiht" sich der andere
Thread nun in die "Warteschlange" ein statt einfach vorbei-zu-laufen,
oder ?!


Hmm, ich weiss nicht, ob sich das Warten vor einer SYNC METHOD wie eine echte Warteschlange verhält (first in, first out). D.h. ob die Zugriffe per Sync Method auf eine bestimmte Variable garantiert in der Reihenfolge abgearbeitet werden, in der sie auflaufen. Diesen Effekt bekommst du aber auf jeden Fall hin, wenn du über PostAppEvent() und AppEvent() die normale Eventqueue verwendest.

Günter
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

Beitrag von AUGE_OHR »

hi,
Günter Beyes hat geschrieben: Oder laufen gleichzeitig vier Mediaplayer, die aus einem bestimmten Grund miteinander synchronisiert werden müssen?
ok ich muss wohl weiter ausholen:

TLB2CH.EXE WMPLAYER.OCX.7 >> WMP.CH

damit erhält man die Events des Mediaplayer v9/v10 in der Datei WMP.CH
sieht man sicht die jetzt an so fallen "Gruppen" auf. Für jeden einzelne
"Gruppe" läuft ein Thread der die Events überwacht. Wird jetzt innerhalb
einer Gruppe (WMPOpenState) ein passender Event (wmposMediaOpen)
ausgelöst so muss ich innerhalb einer anderen Gruppe (WMPPlayState)
auf den dazu gehörigen Event (wmppsPlaying) warten bevor ich die
Methoden ansprechen darf ... sonst knallt es.

der alte "procedurale" Code sah so aus:

Code: Alles auswählen

::oWMP:Settings:AutoStart := .T. // start to play after load
//
// now we load media via :URL
// depending what Codec is need
// it will invoke WMP Event :
// wmppsTransitioning
//
::oWMP:URL  := cFile                      // play via :URL
//
// "resize" parent to same size
//
::oWMP:setparent():setPosAndSize( aPosi  ,{aSize[1],aSize[2]} )
//
// reset fail counter
::failcount := 0
SP_Duration(0)

DO WHILE .T.

   // is Media open ?
   IF ::oWMP:openState = wmposMediaOpen
      //
      // wmppsTransitioning here can fail e.g missing codec
      //  

      // is playing ?
      IF ::oWMP:PlayState = wmppsPlaying

         ::nIMGhigh := ::oWMP:currentMedia:imageSourceHeight
         ::nIMGwide := ::oWMP:currentMedia:imageSourceWidth

         SP_Duration(::oWMP:currentMedia:Duration)

         // ok now we can exit loop
         EXIT
      ENDIF
   ENDIF
   SLEEP(10)
   ::failcount++
   IF ::failcount > 10*5
      // 1st stop it
      ::oWMP:Controls:stop()
      RETURN .F.
   ENDIF
ENDDO
man sieht das SLEEP und das try & Error ... aber in 99% geht es damit :)

nun läuft es also in einzelnen "Gruppen" jeweils als Thread

Code: Alles auswählen

PROCEDURE EVTHREAD(oParent,aoChild, ...)
LOCAL cPosi
LOCAL nZahl := 1
LOCAL nNum  := 0

STATIC lIsplayin := .F.
STATIC nLstPlState := 0

IF SP_IsRUN()                                   // pause Tread before :destroy
   SP_Threadwrk(.T.)                         // signal i´m running

   IF( aoChild[CH_WMP]:openState = wmposUndefined              , EVSCT(aEVTh,01,"T"), EVSCT(aEVTh,01," "))
...
   IF( aoChild[CH_WMP]:openState = wmposMediaOpen              , EVSCT(aEVTh,14,"T"), EVSCT(aEVTh,14," "))
...
/*
hier kommen dann noch die "Prüfungen" des activeX und deren Status

wmposBeginCodecAcquisition                                            
wmposEndCodecAcquisition                                              
wmposBeginLicenseAcquisition                                          
wmposEndLicenseAcquisition                                            
wmposBeginIndividualization                                           
wmposEndIndividualization                                             
wmposMediaWaiting                                                     
*/
...
   IF( aoChild[CH_WMP]:openState = wmposOpeningUnknownURL      , EVSCT(aEVTh,22,"T"), EVSCT(aEVTh,22," "))
...
   SP_Threadwrk(.F.)                      // signal i´m finsh, now :playstate can
ENDIF                                           // lIsRUN
RETURN
Ein DO CASE geht übrigens nicht weil mehrer Zustände gleichzeitig "T"
werden können.

Das selbe dann für :Playstate

Code: Alles auswählen

IF .NOT. SP_Threadwrk()               // if Thread1 is still working do NOT
...
   IF  aoChild[CH_WMP]:PlayState = wmppsPlaying
      EVSCT(aEVTh,26,"T")    // setzte 

      IF EVIST(aEVTh,14,"T") // Abfrage Media ist noch ge”ffnet ?
         IF cInfofile  == SP_Playlast()
         ELSE
             MSGBOX("drin")    // bei 99%
...
/*
      hier kann er bei Problemen im Sinne "hängen" bleiben, aber er
      läuft ja einfach weiter vom activeX aus gesehen ...
*/
   IF  aoChild[CH_WMP]:PlayState = wmppsTransitioning // this event is after open Media
      EVSCT(aEVTh,32,"T")                       // but before wmppsPlaying
      aoChild[CH_STATBAR]:oMSG:setCaption( "Transitioning ..." )
   ELSE
      EVSCT(aEVTh,32," ")
   ENDIF
...
ENDIF
...
soweit sogut bei 99% ... aber bei 1% geht nun was schief beim :PlayState
Thread. Nun ist mein (timeing) Problem, das ja immer erst den Code
durchlaufen bevor :SetInterval(nTime) den Thread neu startet und das
Array dann den letzten Stand behält bis zum nächsten durchlauf.

Beide Thread greifen auf das selbe Array aEVTH zu und erhalten ihren
Status vom activeX Thread der die Events feuert. Der 4st Thread ist
die "play" Funktionen (start/stop/pause/step sowie Fastforward)
sowie die "open" Funktionen (nächste Stück/vorheriges).

Thread 4 wird also vom User kontrolliert und löst damit Reaktionen im
activeX Thread 3 aus in Form von Events. Diese werden nun von dem
beiden ersten Threads "abgehört" und das Array von beiden mit "T"/" "
gefüllt.

erst wenn ich ein bestimmtes Muster im Array habe darf die Reaktion
erfolgen.

bei 99% ist Thread1 wohl rechtzeitig fertig um für Thread2 die richtige
Grundlage zu sein. Bei 1% startet sozusagen Thread2 früher sodas er
einen weiter Durchlauf machen muss um das aktuelle Ergebnis von
Thread1 zu erhalten statt dem "letzten" was sich evtl. geändert hat :(

Im Prinzip müsste ich Thread2 aus Thread1 starten, aber das bekomme
ich nun auch nicht in Griff den es können ja mehrer Event auf "T" stehen
bei den ich dann jeweils (?) einen neuen(?) Thread starten müsste ...

gerne hätte ich ja auch die Idee mit den UserDef Event weiterverfolgt,
aber ein Eventhandler in den Threads reagiert "sehr träge" (wegen der
vielen IF Abfragen ? ).

bei der SYNC Methode hatte ich nun gehofft das sich die Threads schön
in einer Warteschlange "anstellen" und abgearbeitet werden, aber das
scheint ja wohl nicht so (auf meinen Intel PC´s)

ich hoffe nicht zu weit ausgeholt zu haben und die Forum User mit meinem
Problem zu nerven, aber ich hätte gerne auch die 1% gelöst auch wenn es
das original in einer solchen Situation auch nicht viel besser löst und dann
"nach Hause telefonieren" will.

gruss by OHR
Jimmy
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

Beitrag von AUGE_OHR »

hi,

sorry noch mal an alle AMD User : auf einem AMD64 mit XP64 Sp2 (vom
w2003 x64 Server Pack) lief das Demo praktisch 9 von 10 mal während
ich noch keine Intel Machine gefunden habe die mir annähernd diese
Treffer Quote gibt. Kann das wirklich sein das der hier besprochene Code
verschiedene Ergebnisse ergibt ?

gruss by OHR
Jimmy
Antworten