Seite 1 von 6
Strings in Threads ohne "Threadsafe"
Verfasst: 12.11.2008 00:07
von Fluid Byte
Ich habe mit Threads bisher noch nicht wirklich viel gemacht weiß aber das eine Menge Probleme mit sich bringen können. Der Zweck meines Programms soll sein eine Datenbank im Hintergrund auszulesen. Ich hab' was zusammengestrick allerdings ist es Glückssache ob es abschmiert oder nicht.
Code: Alles auswählen
EnableExplicit
Declare DataAccessProc(lParam)
Define Highscore
Define i, DataAccessing, ThreadID
Procedure.s MyFunction(Wert1,Wert2)
; anderer Kram
EndProcedure
For i=1 To 9999
DataAccessing = 1
If IsThread(ThreadID) : KillThread(ThreadID) : EndIf
ThreadID = CreateThread(@DataAccessProc(),@DataAccessing)
Next
Procedure DataAccessProc(lParam)
Protected HighScore$ = MyFunction(1,20)
PokeL(lParam,0)
EndProcedure
Einfach den Code ein paar mal hintereinander ausführen, wird irgendwann abschmieren.
Die Frage ist also was mach ich verkehrt?
Re: Thread crashed Programm an diversen Stellen
Verfasst: 12.11.2008 00:31
von Kiffi
Fluid Byte hat geschrieben:Die Frage ist also was mach ich verkehrt?
KillThread() sollte nach Möglichkeit nur in absoluten Notfällen eingesetzt
werden. Weitere Infos in der PB-Hilfe.
Grüße ... Kiffi
Verfasst: 12.11.2008 00:32
von HeX0R
Da du einen String in deiner Threadprozedur benutzt, solltest du Threadsave aktivieren.
Ausserdem ein besseres Beispiel machen

Den Thread sofort wieder zu killen, wenn er gerade mal so gestartet ist, ist nicht wirklich gut.
Im Prinzip, sollte KillThread eh nur in Ausnahmefällen benutzt werden, ein Thread lässt sich auch vernünftig beenden (schon klar, dass das nur ein zusammengepfriemeltes Beispiel ist).
Verfasst: 12.11.2008 00:43
von HeX0R
Hier mal ein Beispiel, wie man einen Thread normalerweise beenden sollte und wie man KillThread benutzt:
Code: Alles auswählen
Structure THREADVALUE
ThreadID.i
StopIt.i
Bla.l
Blubb.s
EndStructure
Procedure MyThread(*TV.THREADVALUE)
;
Repeat
Delay(5)
If *TV\StopIt
Break
EndIf
ForEver
*TV\ThreadID = #False
EndProcedure
Define.THREADVALUE T
T\ThreadID = CreateThread(@MyThread(), @T)
OpenWindow(0, 0, 0, 100, 100, "", $C8<<16)
While WaitWindowEvent() ! 16 : Wend
;Now Close Thread
T\StopIt = #True
If T\ThreadID And WaitThread(T\ThreadID, 500) = 0
;Ok, this thread seems to be dead (endless loop?)
Debug "killed ?"
KillThread(T\ThreadID)
EndIf
Wie man sieht, ist KillThread nur noch die Notbremse, falls der Thread in der Endlosschleife feststeckt (was ja letztendlich dann auch an einem selbst liegt).
Verfasst: 12.11.2008 18:34
von Fluid Byte
HeX0R hat geschrieben:Da du einen String in deiner Threadprozedur benutzt, solltest du Threadsave aktivieren.
Gerade das möchte ich vermeiden. Mit Threadsafe aktiviert funktioniert zwar mein obiges Snippet aber geht auf Kosten der Geschwindigkeit.
Ist diese Compilereinstellung zwingend notwending wenn ich Strings verwende?
Verfasst: 12.11.2008 18:55
von Thorium
Fluid Byte hat geschrieben:HeX0R hat geschrieben:Da du einen String in deiner Threadprozedur benutzt, solltest du Threadsave aktivieren.
Gerade das möchte ich vermeiden. Mit Threadsafe aktiviert funktioniert zwar mein obiges Snippet aber geht auf Kosten der Geschwindigkeit.
Ist diese Compilereinstellung zwingend notwending wenn ich Strings verwende?
Nicht zwingend, ist aber zu empfehlen. Du kannst den String auch selbst schützen. Du kannst dafür einen Mutex verwenden.
Beispiel aus der PB-Hilfe:
Code: Alles auswählen
; Starten Sie diesen Code so wie er ist. Sie werden sehen, das die Zeilen
; gemischt durch die Threads ausgegeben werden. Jetzt entfernen Sie den
; Kommentar vor den Mutex-Befehlen und die Strings werden in Reihenfolge
; ausgegeben, da nur ein Thread zur gleichen Zeit das Recht zum Ausführen
; der Print-Befehle bekommt.
;
Procedure WithoutMutex(*Number)
Shared Mutex
For a = 1 To 5
;LockMutex(Mutex) ; entfernen Sie das ';' um den Unterschied zu sehen
PrintN("Thread "+Str(*Number)+": Trying to print 5x in a row:")
For b = 1 To 5
Delay(50)
PrintN("Thread "+Str(*Number)+" Line "+Str(b))
Next b
;UnlockMutex(Mutex) ; entfernen Sie das ';' um den Unterschied zu sehen
Next a
EndProcedure
OpenConsole()
Mutex = CreateMutex()
thread1 = CreateThread(@WithoutMutex(), 1)
Delay(25)
thread2 = CreateThread(@WithoutMutex(), 2)
Delay(25)
thread3 = CreateThread(@WithoutMutex(), 3)
WaitThread(thread1)
WaitThread(thread2)
WaitThread(thread3)
Input()
Das selbst schützen scheint aber gerade bei Strings nicht immer wasserdicht zu sein. Hatte in meinem asyncronen Ressourceloader auch alles schön mit Mutexen (keine Ahnung was die Mehrzahl ist) gesichert. Trotzdem musste ich Threadsave aktivieren, da es Crashes gab. Zwar sehr selten aber Crash ist Crash und kann nicht tolleriert werden.

Verfasst: 12.11.2008 20:20
von Fluid Byte
Thorium hat geschrieben:Du kannst den String auch selbst schützen. Du kannst dafür einen Mutex verwenden.
Ich habe in meinem obigen Code mal für Testzwecke
Code: Alles auswählen
For i=1 To 9999
DataAccessing = 1
If IsThread(ThreadID) : KillThread(ThreadID) : EndIf
ThreadID = CreateThread(@DataAccessProc(),@DataAccessing)
Next
durch
Code: Alles auswählen
For i=1 To 9999
DataAccessing = 1
If IsThread(ThreadID) = 0
ThreadID = CreateThread(@DataAccessProc(),@DataAccessing)
EndIf
Next
ersetzt und es funktioniert. Das klappt aber nur in diesem Democode und nicht in meinem eigentlichen Programm. Und ich weiß auch warum. Folgt nach der Erstellung des Threads noch weiterer Programmcode schmiert es ab, auch mit Mutexes.
Code: Alles auswählen
EnableExplicit
Global Mutex = CreateMutex()
Declare DataAccessProc(lParam)
Define Highscore
Define i, DataAccessing, ThreadID, TempDir$
Procedure.s MyFunction(Wert1,Wert2)
; anderer Kram
EndProcedure
For i=1 To 9999
DataAccessing = 1
If IsThread(ThreadID) = 0
ThreadID = CreateThread(@DataAccessProc(),@DataAccessing)
TempDir$ = GetTemporaryDirectory()
EndIf
Next
Procedure DataAccessProc(lParam)
LockMutex(Mutex)
Protected HighScore$ = MyFunction(1,20)
PokeL(lParam,0)
UnlockMutex(Mutex)
EndProcedure
Thorium hat geschrieben:Das selbst schützen scheint aber gerade bei Strings nicht immer wasserdicht zu sein.
Ich denke da scheint der Hund begraben zu sein. Ich erinnere mich noch an meinen Mediaplayer der im Hintergrund die MP3 Tags auslesen sollte. Hatte da dieselben Probleme. Habe auch versucht den Code aufs Wesentliche zu reduzieren und Mutexes zu benutzen aber vergebens. Nur mit Threadsafe funktioniert die Anwendung einwandfrei.
Verfasst: 12.11.2008 20:28
von Thorium
Fluid Byte hat geschrieben:
Code: Alles auswählen
EnableExplicit
Global Mutex = CreateMutex()
Declare DataAccessProc(lParam)
Define Highscore
Define i, DataAccessing, ThreadID, TempDir$
Procedure.s MyFunction(Wert1,Wert2)
; anderer Kram
EndProcedure
For i=1 To 9999
DataAccessing = 1
If IsThread(ThreadID) = 0
ThreadID = CreateThread(@DataAccessProc(),@DataAccessing)
TempDir$ = GetTemporaryDirectory()
EndIf
Next
Procedure DataAccessProc(lParam)
LockMutex(Mutex)
Protected HighScore$ = MyFunction(1,20)
PokeL(lParam,0)
UnlockMutex(Mutex)
EndProcedure
Das der Mutex da nix bringt ist klar. Da du ja nur von einer Stelle aus auf die Highscore zugreifst. Ein Mutex bringt nur was, wenn mehrere Codestellen auf die gleiche Ressource (z.b. String) zugreifen. Dann kannst du beide mittels Mutex dazu bewegen aufeinander zu warten anstatt direkt zuzugreifen. Wenn du nur in dem einen Thread auf die Highscore zugreifst und sonst nirgends, wirst du um Threadsave nicht drummherum kommen. Dann greift PB intern auf irgendwas mehrfach gleichzeitig zu.
Verfasst: 12.11.2008 20:35
von Thorium
Mir fällt grad auf das der Hund natürlich auch in:
Procedure.s MyFunction(Wert1,Wert2)
; anderer Kram
EndProcedure
begraben sein kann.
Jenachdem was da gemacht wird in MyFunction und auf was zugegriffen wird. Du hast ja praktisch nur den Aufruf von MyFunction gesichert und das auch nur in dem Highscore-Thread / den Highscore-Threads.
Verfasst: 12.11.2008 21:05
von Fluid Byte
Gut möglich aber ich würde gerne erstmal den Democode zum laufen bringen.
Außerdem hab' ich vergessen zu erwähnen das wenn man die Zeile
auskommentiert funktioniert der Code wieder einwandfrei. Die Variable 'TempDir$' wird außerhalb des Threads deklariert und gesetzt. Warum schmiert das Programm dann aber trotzdem ab?