Thread - Synchronisation
In vielen Fällen muss der Thread auf Daten zugreifen die auch vom Haupthread oder einem anderen Thread benötigt werden. Dieser Zugriff muss geschützt werden, bzw. der abwechselnde Zugriff synchronisiert.
Zugriffe auf Atomic - Variablen (x86: Byte,Word,Character,Long,Float; x64: +Double,Quad) müssen nicht geschützt werden. Hierbei kann aber das Readers - Writers Problem auftauchen, hierzu später im Tutorial.
Mal ein kleines Beispiel eines Konflikt beim Zugriff auf eine geteilte Ressource:
Code: Alles auswählen
; Unsere altbekannte Thread-Data Strukture
Structure Thread_Data
Res.i
EndStructure
Procedure Thread(*Thread.Thread_Data)
; Schreiben
For i = 1 To 10000
WriteStringN(*Thread\Res, "-----------------------------------------------------------------")
Next
EndProcedure
; Auch den Thread Launcher kennen wir schon
Procedure Thread_Laucher(Res)
Protected *Thread.Thread_Data = AllocateMemory(SizeOf(Thread_Data))
*Thread\Res = Res
ProcedureReturn CreateThread(@Thread(), *Thread)
EndProcedure
File = CreateFile(#PB_Any, GetTemporaryDirectory() + "tmp_123.tmp")
Thread = Thread_Laucher(File)
; Schreiben
For i = 1 To 100
WriteStringN(File, "-----------------------------------------------------------------")
Next
CloseFile(File)
Vielleicht kein ideales Beispiel aber mir ist einfach nichts anderes eingefallen. In diesem Beispiel versuchen der Haupt und ein NebenThread gleichzeitig in dieselbe Datei zu schreiben. Nun wird die Datei später nicht so aussehen wie gewünscht, Zeilen werden fehlen, Zeilen werden falsch sein, und der Effekt verstärkt sich noch wenn man mehr Threads verwendet:
Code: Alles auswählen
; Unsere altbekannte Thread-Data Strukture
Structure Thread_Data
Res.i
EndStructure
Procedure Thread(*Thread.Thread_Data)
; Schreiben
For i = 1 To 10000
WriteStringN(*Thread\Res, "-----------------------------------------------------------------")
Next
EndProcedure
; Auch den Thread Launcher kennen wir schon
Procedure Thread_Laucher(Res)
Protected *Thread.Thread_Data = AllocateMemory(SizeOf(Thread_Data))
*Thread\Res = Res
ProcedureReturn CreateThread(@Thread(), *Thread)
EndProcedure
File = CreateFile(#PB_Any, GetTemporaryDirectory() + "tmp_123.tmp")
Thread1 = Thread_Laucher(File)
Thread2 = Thread_Laucher(File)
Thread3 = Thread_Laucher(File)
Thread4 = Thread_Laucher(File)
; Schreiben
For i = 1 To 100
WriteStringN(File, "-----------------------------------------------------------------")
Next
CloseFile(File)
Deswegen müsse wir den Zugriff synchronisieren. Hierzu verwenden wir eine Mutex. Eine Mutex ist ein Objekt das immer nur von einem Thread gesperrt werden kann, alle anderen müssen warten bis der Thread die Mutex wieder freigibt.
Ungefähr zu vergleichen mit einem Raum zu dem es nur einen Schlüssel gibt, immer nur eine Person kann rein, dann fällt die Tür wieder ins Schloss und keiner kann rein bis Sie wieder rauskommt und den Schlüssel weitergibt.
Hierbei kann bei unsauberer Programmierung zu einem Death-Lock kommen, auch hierzu später im Tutorial.
Das Unseres Codes würde so gehen, natürlich übergeben wir die Mutext mit unseren Thread - Parametern:
Code: Alles auswählen
; Unsere altbekannte Thread-Data Strukture
Structure Thread_Data
Res.i
Mutex.i ; Schutzmutex
EndStructure
Procedure Thread(*Thread.Thread_Data)
LockMutex(*Thread\Mutex) ; Mutex sperren
; Schreiben
For i = 1 To 10000
WriteStringN(*Thread\Res, "-----------------------------------------------------------------")
Next
UnlockMutex(*Thread\Mutex) ; Mutex freigeben
FreeMemory(*Thread)
EndProcedure
; Auch den Thread Launcher kennen wir schon
Procedure Thread_Laucher(Res, Mutex)
Protected *Thread.Thread_Data = AllocateMemory(SizeOf(Thread_Data))
*Thread\Res = Res
*Thread\Mutex = Mutex ; Mutex übergeben
ProcedureReturn CreateThread(@Thread(), *Thread)
EndProcedure
Mutex = CreateMutex() ; Mutex erstellen
File = CreateFile(#PB_Any, GetTemporaryDirectory() + "tmp_123.tmp")
Thread1 = Thread_Laucher(File, Mutex)
Thread2 = Thread_Laucher(File, Mutex)
Thread3 = Thread_Laucher(File, Mutex)
Thread4 = Thread_Laucher(File, Mutex)
LockMutex(Mutex) ; Mutex sperren
; Schreiben
For i = 1 To 100
WriteStringN(File, "-----------------------------------------------------------------")
Next
UnlockMutex(Mutex) ; Mutex freigeben
WaitThread(Tread1)
WaitThread(Tread2)
WaitThread(Tread3)
WaitThread(Tread4)
FreeMutex(Mutex)
CloseFile(File)
Natürlich kann die Mutex eine gewaltige Performance-Bremse sein, wenn der Thread die ganze zeit nur auf die Mutex wartet, hier währe man wahrscheinlich mit linearem Schreiben schneller.
Leider gibt es bei Threads keine eierlegende Wollmilchsau, man muss die Synchronisation immer an den Einzelfall anpassen.