FAQ: Threads in PB

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

FAQ: Threads in PB

Beitrag von PMV »

Folgender Text wurde von mir in Fließarbeit erstellt und nicht auf Rechtschreib- oder Gramatikfehler überprüft. Der Inhalt ist mit Quellen belegt. Aufgrund meiner Zeitknappheit komm ich so schnell nicht dazu, den Text zu verbessern/ erweitern und Poste ihn daher "unfertig". Zusätzliche Inhalte können gerne dazu gepostet werden. Sollte ich irgend wann mal wieder die Zeit dazu finden werd ich das dann auch dazu schreiben und eventuell sogar ins englische übersetzen ... obwohl das vermutlich eh besser wer anders machen sollte /:->

-------------------------------------------------------------
Stand PB4.20 + PB4.30 B4

Ich bin Anfänger, kann ich trotzdem Threads verwenden?
Threads bedeuten ein hohes Risiko für die Programmstabilität. Threads gehören zu einem der letzten Kapitel, die ein PB-Programmierer lernen sollte. Um mit Threads sauber arbeiten zu können muss vor dem Programmieren genau überlegt werden, was benötigt wird und wie das Programm funktionieren soll. Fehlerhaftes nutzen von Threads führt in jedem Fall zur instabilität eines Programms und damit meistens undefinierten Fehlern!



Wofür sind Threads?
In prozeduralen Sprachen wie PureBasic wird jedes Programm von "oben nach unten" abgearbeitet. Dabei fängt das Programm in der Zeile an, in der der erste ausführbare Quellcode zu finden ist. Mit Schleifen, If-Anweisungen und Prozeduren kann der Programmfluss beeinflusst werden, in jedem fall wird aber zum gleichen Zeitpunkt immer nur ein Befehl ausgeführt. Wird ein Thread benutzt, ändert sich dies, da jeder Thread seinen eigenen Programmfluss unabhänig vom Hauptthread hat. Durch Threads können also in der theorie mehrere Befehle gleichzeitig ausgeführt werden.



Was macht die Compileroption Threadsafe?
Die Behandlung von Strings und einigen PB-Befehle werden intern geändert. Strings haben normal einen globalen Speicherpuffer für sämtliche Operationen. Diese Lösung führt bei Threads aber zu großen Problemen. Mit der Compileroption bekommt jeder Thread seinen eigenen Puffer (localstring storage), wodurch das Problem gelöst wird. Diese Lösung geht aber auf Kosten der Geschwindigkeit, weshalb diese Compileroption auch nur bei Threads verwendet werden sollte. Desweiteren bestehen die PB-Befehele logischerweise nicht aus einzellnen Anweisungen, sondern aus teilweise sogar sehr komplexen Befehlsketten, die ohne weiteres nicht gleichzeitig von mehreren Orten aus verwendet werden dürfen. Durch Threadsafe werden also alle weiteren PB-Befehle intern gesichert.



Was muss ich bei der Verwendung von Threads beachten?
In eigentlich jedem Fall ist "Threadsafe" zu aktivieren, da das manuelle Sichern sämtlicher Stringoperationen den Sinn eines Threads aushebeln würde und kaum einer den überblick hat, welche PB-Befehle sonnst noch gesichert werden müssen.

Fenster in Threads sind ein weiteres spezielles Thema:
- Unter Windows muss jeder Thread, der seine eigenen GUI-Elemente erstellt einen Eventloop haben. Das heißt WindowEvent() muss in dem entsprechenden Thread genutzt werden, wenn z.b. Gadgets, Toolbars, Fenster, Menüs oder eine Statusbar genutzt wird.
- Unter Linux hingegen darf nur der Hauptthread eine Eventloop haben. Auch GUI-Elemente sollten nach Möglichkeit nur dort erstellt werden.
- Unter Mac OS X sind Eventloops ebenfalls nur im Hauptthread erlaubt.
Gadgets dürfen nur auf Fenster des eigenen Threads erstellt werden, da die Eventbehandlung sonnst durcheinander kommt. Für die Platformunabhängigkeit sollte also der Hauptthread alles erstellen und Threads greifen nur verändernd auf diese Elemente zu.

DirectX darf in nur einem Thread verwendet werden, das ist eine Einschränkung von Microsoft. Egal ob es der Hauptthread ist oder ein anderer, es müssen allerdings sämtliche Init-Befehle die damit zu tun haben in dem Thread aufgerufen werden, der diese Befehle benutzt.

Der Befehl KillThread() existiert zwar, darf aber nur in wirklichen Notfällen genutzt werden. Wird ein Thread trotzdem mit diesem Befehl zum abstürzen gebracht, können Ressorcen nicht aufgeräumt werden. Selbst Microsoft warnt davor, diesen Befehl zu verwenden. Der wirklich einzige Zweck, den dieser Befehl hat, ist fals sich ein Thread aufhängt ihn trotzdem beenden zu können, z.b. weil er die CPU auslastet.

Bei den Netzwerkbefehlen ist das Erstellen und Schließen eines Servers nicht sicher, genau so wie dessen Ereignisbehandlung dürfen mehrere Threads nicht gleichzeitig diese Befehle nutzen können. Für Multiserverprogramme müssen die Serverfunktionen also manuell abgesichert werden.

Der Debugger von PureBasic arbeitet bei Threads ohne Threadsafe nicht richtig. Zwar sollte es ohne Threadsafe kein Absturz geben, allerdings dürfen dann nach "Debug" keine Stringoperationen getätigt werden. Die Informationen des Debuggers sind aber sehr wahrscheinlich ohne Threadsafe falsch, z.B. die Zeilennummer.

Globale/ geteilte Ressorcen müssen zusätzlich geschützt werden. Ausgenommen sind nur die einfachen Variablentypen Byte, Word, Long, Float, Zeiger und Integer. Nur in x64 Programmen sind auch Quads und Doubles unproblematisch. Wird mit diesen Variablen in mehreren Threads gearbeitet, sind keine Probleme zu befürchten. Bei komplexeren Datenstrukturen ist immer vorsicht geboten, da hier nur ein Thread zur selben zeit zugreifen darf.

Die Konsole ist eine geteile Ressorce. Da PB das nicht selber macht muss auch der Zugriff auf diese geschützt werden. (ändert sich das noch in PB V4.30?)



Wie schütze ich globale/ geteilte Ressorcen?
PureBasic bietet in der Threadbibliothek zusätzliche Befehle um kritischen Programmcode mit einem Mutex zu schützen. Ein Mutex ist ein spezielles Objekt, dass zur selben Zeit immer nur von einem Thread gesperrt werden kann. Hat ein Thread also ein Mutex erfolgreich gesperrt, hat kein anderer Thread diesen Mutex gesperrt und es kann bedenkenlos auf die entsprechende Ressorce zugegriffen werden. Nach dem Zugriff muss der Thread das Mutex wieder entsperren. Ein Mutex wird durch die Programmlogik einer Ressorce zugewiesen, das heißt der Programmierer muss selber wissen, welches Mutex gesperrt werden muss, um sicher auf eine geteilte Ressorce zugreifen zu können. Das Sperren muss für jeden, wirklich jeden Zugriff auf die Ressorce getätigt werden, sobald mehrere Threads diese Ressorce gleichzeitig nutzen.



Was sind Deadlocks und wodurch entstehen diese?
Wartet Thread A auf ein Mutex, welches von Thread B gesperrt wurde und wartet gleichzeitig Thrad B auf ein Mutex, das von Thread A bereits gesperrt wurde kann die Programmausführung nicht mehr fortgesetzt werden. Dies nennt man Deadlock. Um dieses Problem zu vermeiden ist es notwenig, Mutex immer in der selben Reihenfolge zu sperren. Gibt es z.B. 3 Stück: A, B und C und man stellt sicher, das immer zuerst A, dann B und zuletzt C gesperrt werden, können solche Deadlocks nicht durch das Sperren einstehen. Werden A, B und C unabhängig voneinander benutzt, also kein Thread sperrt gleichzeitig 2 oder 3 Stück, so ist ebenfalls ein Deadlock ausgeschlossen.



In welchen Situationen sind Threads zu empfehlen?
Eine direkte Empfehlung gibt es wohl nicht. Die meisten Probleme für die Threads in frage kommen, sind auch genau so gut ohne lösbar. Der Sinn oder Unsinn dahinter liegt letztendlich im Auge des Programmierers, der sich selber wirklich gut überlegen sollte, ob Threads wirklich einen größeren Vorteil bieten. Bevor sich jemand allerdings wirklich mit diesem Thema beschäftigt sollte wohl einiges an Erfahrung auf zu weisen sein.

Rechenintensive Operationen, die sich nicht kurzeitig unberbrechen lassen wären allerdings ein guter Grund, da der Benutzer sonnst schnell den Eindruck bekommt, das Programm wäre abgestürzt. Situationen mit verschiedenen Antwortzeiten oder Intervallen können auch ein Grund für Threads sein. Und dank der heutigen Multicore(Mehrkern)-Prozessoren sind Threads für rechenintensive Operationen besonders Empfehlenswert, sofern sich diese auf mehrere Kerne aufteilen lassen, da jeder Kern im Idealfall einen anderen Thread ausführen kann und das Programm ohne Threads nur einen Kern zur verfügung hat.




Quellen:
- GUI in Windows und Linux:
http://www.purebasic.fr/german/viewtopi ... 18&start=0
- GUI in Mac OS X
http://www.purebasic.fr/english/viewtop ... 13&t=45422
- KillThread()
http://www.purebasic.fr/german/viewtopi ... 08&start=0
- globale Stringverwaltung
http://www.purebasic.fr/german/viewtopi ... 8&start=30
- Was ist die Idee hinter Threadsafe?
http://www.purebasic.fr/german/viewtopi ... 6&start=10
- Netzwerkbefehle/ Debugger/ Konsole
http://www.purebasic.fr/english/viewtopic.php?t=31803
- geteilte Ressorcen
http://www.purebasic.fr/english/viewtopic.php?t=29581
Zuletzt geändert von PMV am 15.04.2012 22:45, insgesamt 2-mal geändert.
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
X360 Andy
Beiträge: 1206
Registriert: 11.05.2008 00:22
Wohnort: Bodensee
Kontaktdaten:

Beitrag von X360 Andy »

Cool danke für die Info !
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

DirectX darf in keinem Thread verwendet werden, das ist eine Einschränkung von Microsoft
Das ist falsch, man darf es sehr wohl. Man sollte allerdings nur einen
Thread nutzen, Mainthread oder einen erstellten Thread ist dabei egal.

Wer mehr ueber Threads wissen moechte (von wegen Sinn usw) sollte mal
im "Petzold" nachschlagen.
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Ich hab die PB-Hilfe lediglich umformuliert, aber du hast natürlich recht,
hab ich ja auch schon anders benutzt ^_^ ... ich änder das entsprechend
ab. Mal davon abgesehen das die Forumlierung auch unlogisch war, denn
der Hauptthread ist ja auch ein Thread. :lol:

MFG PMV
Zuletzt geändert von PMV am 27.11.2008 00:22, insgesamt 1-mal geändert.
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Beitrag von NicTheQuick »

Doubles gehören meines Wissens auch zu den einfachen Datentypen, die keine Probleme
in Threads machen. Quads bestimmt auch, aber da muss man mal die ASM-Freaks hier
fragen.
Bild
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Hab ich Freak bereits gefragt und er hat mir das so bestätig.

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Beitrag von Thorium »

NicTheQuick hat geschrieben:Doubles gehören meines Wissens auch zu den einfachen Datentypen, die keine Probleme
in Threads machen. Quads bestimmt auch, aber da muss man mal die ASM-Freaks hier
fragen.
Nene, is schon richtig so. Um auf Quads zuzugreifen auf einem 32bit System sind 2 Instuktionen nötig. Deswegen nicht save.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Re: FAQ: Threads in PB

Beitrag von PMV »

Fred hat im englischen Forum erwähnt, das auch unter
Mac OS X der Eventloop für Fenster nur im Hauptthread sein darf.
Den kleinen Eintrag hab ich nun entsprechend hinzugefügt.
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
Max_der_Held
Beiträge: 594
Registriert: 18.04.2006 17:01
Wohnort: Bavaria
Kontaktdaten:

Re: FAQ: Threads in PB

Beitrag von Max_der_Held »

Mit Verlaub, damit ich das richtig verstanden habe:

Ich muss derzeit keinen Zugriff auf globale Strings Mutex-schützen!?
ein "threadsafe" reicht voll aus?
(bisher, zumindest, hatte ich da keine Probleme :) )

Code:

Code: Alles auswählen

Global text.s 

Procedure hallo ( void)
    startzeit = ElapsedMilliseconds() + 5000
    Repeat 
    text = Str(Random ( 50000))
    Delay(1)
    PrintN(text)
    Until startzeit < ElapsedMilliseconds() 
EndProcedure 

OpenConsole ()
For x = 1 To 50
CreateThread ( @hallo () , 0)
Next 

Input ()
Mfg Max
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6994
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: FAQ: Threads in PB

Beitrag von STARGÅTE »

Vorsicht, du vermischst in deinem Beispiel jetzt verschiedene Sachen.

Thread-Safe ist einzig und alleine dafür da, dass sichergestellt wird, dass der allgemeine Umgang mit Strings sicher ist (selbst wenn sie protected wären).
Wäre Thread-Safe aus, könnten sogar IMAs entstehen, obwohl der String garnicht global ist.
Bei Thread-Safe geht es nur darum, dass ein String (wenn er zB erweitert wird) nicht in den Speicher missbraucht, den gerade ein andere Thread für seinen String nützen möchte.

Wenn es darum geht gleichzeitig eine globale String-Variable zu beschreiben, dann ist Mutex pflicht!

Dei Beispiel erzeugt bei mir zB totalen Müll und ein Overflow.
8589
8589
8589
8589
­¡♂1135
­¡♂1135
­¡♂1135
­¡♂1135
­¡♂1135
26223
26223
26223
Mutex muss nicht sein, wenn es um Typen wie Long, Integer, Float usw. geht, da diese mit einem Zyklus geschrieben oder gelesen werden.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten