Tutorial
Threading-Optimierung
von ThePuppetMaster | Seite 6 von 6 |
Der nächste "Angriffspunkt" bei thredbasierter Optimierung ist der ungeschützte wechselseitige Zugriff auf gemeinsame Ressourcen im Lesebetrieb.
Grundsätzlich sei gesagt, das Schreibzugriffe auf gemeinsame Ressourcen IMMER geschützt werden müssen, und es hier keinen Weg daran vorbei gibt. Dies gilt ebenfalls für lesende Aufrufe.
Einzigste Möglichkeit der Optimierung stellt hier eine komplexe Speicherverwaltung dar, welche über einen komplexen Speicherbereich den Zugriff auf die selben Ressourcen, quasi klonend, ermöglicht.
Wichtig ist hier zu erwähnen, das solche Optimierungen ausschließlich die Geschwindigkeit berücksichtigen und den Speicherverbrauch gänzlich vernachlässigen bzw. Ignorieren.
Angenommen es existieren mehrere Threads, welche deutlich häufiger auf ein und die selbe Ressource lesend zugreifen als schreibend. Dann wäre es von Vorteil ein Mechanismus zu entwickeln, der es ermöglicht ohne Mutexe den Lesevorgang abzuwickeln.
Ein Solches Prinzip möchte ich hier erarbeiten und vorstellen.
Zuerst einmal ist ein Thread nötig, welcher relativ häufig auf gemeinsame Ressourcen lesend zugreift und nur relativ selten schreibende Operationen durchführt.
Dim Shared GlobVar as UInteger
Sub Thread()
Do
MutexLock(XMutex)
If GlobVar = 10 Then
GlobVar = 15
ElseIf GlobVar = 100 Then
GlobVar = 115
ElseIf GlobVar = 1000 Then
GlobVar = 1015
End If
MutexUnLock(XMutex)
Sleep 1, 1
Loop
End Sub
Das Hauptprogramm hingegen ändert nach einer zufälligen Zeit die Variable ab.
Dim C as UInteger
Dim X as UInteger
Do
C += 1
If X < C Then
MutexLock(XMutex)
GlobVar += 1 MutexLock(XMutex)
C = 0
X = Int((Rnd * 1000) + 1)
End If
Sleep 1, 1
Loop
Dieser Thread liest ständig "GlobVar" aus und überprüft ob "GlobVar" einen bestimmten Wert angenommen hat. Ist dies der Fall, dann wird "GlobVar" abgeändert.
Dabei liest der Thread weitaus öfter "GlobVar" aus als er X setzt. Hierbei wird jedoch viel Zeit verschwendet, während das Mutex gesperrt ist.
Es wäre also hilfreich, wenn eine Möglichkeit bestünde "GlobVar" nicht sperren zu müssen um dessen Zustand zu prüfen. Führt man sich einmal den Ablauf dieses Codes vor Augen, erkennt man das Potenzial "GlobVar" einfach auszulagern.
Diese "Auslagerung" ist jedoch nicht einfach und erfordert einiges an zusätzlichem Codeaufwand. Dieser rechtfertigt sich jedoch nur bei wirklich Zeitintensiven Programmaufgaben, dessen Threads "relativ" lange Laufzeiten besitzen.
Zuerst wird der Code für die wechselseitige Zugriffsverwaltung erzeugt.
'Eine Typenstruktur erzeugen. Sie hält Informationen für den Redirect bereit.
Type Memory_Type
V_Next as Memory_Type Ptr
V_Prev as Memory_Type Ptr
V_Source as Any Ptr
V_Redirect as Any Ptr
V_Mutex as Any Ptr
End Type
'Linkedlist zur Speicherverwaltung des Managements
Dim Shared Memory_First as Memory_Type Ptr
Dim Shared Memory_Last as Memory_Type Ptr
Dim Shared Memory_Mutex as Any Ptr
'Sub welche eine Variable mit einer globalen Variable definiert.
Sub Memory_Regist(V_SourceVar as Any Ptr, ByRef V_DestVar as Any Ptr, ByRef V_VarMutex as Any Ptr)
MutexLock(Memory_Mutex)
If Memory_Last 0 Then
Memory_Last->V_Next = CAllocate(SizeOf(Memory_Type))
Memory_Last->V_Next->V_Prev = Memory_Last
Memory_Last = Memory_Last->V_Next
Else
Memory_Last = CAllocate(SizeOf(Memory_Type))
Memory_First = Memory_Last
End If
With *Memory_Last
.V_Source = V_SourceVar
.V_Redirect = V_DestVar
.V_Mutex = MutexCreate()
V_VarMutex = .V_Mutex
End With
MutexUnLock(Memory_Mutex)
End Sub
'Sub welche einen Wert global speichert. Diese Sub muss entsprechend Überladen werden, wenn andere Datentypen genutzt werden sollen.
Sub Memory_Set(V_DestVar as Any Ptr, ByRef V_Value as UInteger)
MutexLock(Memory_Mutex)
Dim XSource as Any Ptr
Dim TPtr as Memory_Type Ptr = Memory_Last
Do Until TPtr = 0
If TPtr->V_Redirect = V_DestVar Then XSource = TPtr->V_Source: Exit Do
Loop
If XSource = 0 Then MutexUnLock(Memory_Mutex): Exit SubTPtr = Memory_Last
Do Until TPtr = 0
If TPtr->V_Source = XSource Then
MutexLock(TPtr->.V_Mutex)
*Cast(UInteger Ptr, TPtr->V_Redirect) = V_Value
MutexUnLock(TPtr->.V_Mutex)
End If
Loop
MutexUnLock(Memory_Mutex)
End Sub
Diese Strukturierung hinterlegt in einer Linkedlist den Zielpointer mit dem Quellpointer.
Der Thread muss hier natürlich entsprechend angepasst werden.
Sub Thread()
Dim LocVar as UInteger
Dim XMutex as Any Ptr
Memory_Regist(@GlobVar, @LocVar, XMutex) 'Hier wird die Globale Variable, die gemeinsam verwendet werden soll, mit der Lokalen Variable assoziiert.
Do
MutexLock(XMutex)
If LocVar = 10 Then
Memory_Set(LocVar, 15)
ElseIf LocVar = 100 Then
Memory_Set(LocVar, 115)
ElseIf LocVar = 1000 Then
Memory_Set(LocVar, 1015)
End If
MutexUnLock(XMutex)
Sleep 1, 1
Loop
'Vor dem Ende mus die Variablen-regestrierung wieder aufgehoben werden!
'Ansonsten werden beim "Memory_Set" Speicherzugriffsfehler auftreten!
End Sub
Hier entfällt das Mutex für GlobVar vollständig da der nötige Schutz in der entsprechenden Memory_Set Sub schon integriert ist und Leseaktionen über separate Speicherbereiche ablaufen.
Zum lesen wird eine Lokale Variable genutzt, welche in der Linkedlist mit der Globalen "verlinkt" wird. Die globale Variable dient hier ausschließlich der Identifikation, und nicht mehr dem Zugriff.
Wird nun der Wert der lokalen Variable per Memory_Set geändert, wird der neue Wert automatisch in alle anderen registrierten Variablen hinterlegt.
Um die Lokale Variable zu lesen nutzt man ein Seperates Mutex, das ausschliesslich für diese Variable vorhanden ist.
Ansich ist es ein massiver Speicherverbrauch. Man könnte auch den Eindruck gewinnen das die abarbeitung noch langsamer ist als mit nur einem Mutex. Das ist jedoch nicht ganz richtig.
Dadurch, das jede Lokale Variable ein eigenes Mutex besitzt wird diese variable auch nicht durch andere Lesende Threads beeinflusst oder Blockiert.
Das Mutex welches hier verwendugn findet, dient ausschliesslich dem Schutz bei Schreibzugriffe auf die Variable. Zu anderen Zeiten (Lesende Zeiten) ist die Variable ausschliesslich für einen Thread gültig und wird so nich behindert.
Beim aufruf von "Memory_Set" wird das entsprechende Mutex der Lokalen Variable nur für die kurze schreibaktion Blockiert. In dieser Zeit können jedoch alle anderen Threads mit dem Wert Ihrer Variable weiter arbeiten.
Die Änderung für andere Threads tritt erst dann ein, wenn dessen Variable aktualisiert wurde.
Auf keinen Fall sollte man das DeRegistrieren der Lokalen Variable aus der LinkedList bevor der Thread sich beendet. Sonst kann ein anderer Thread, der über "Memory_Set" Daten verändert auf Variablen zugreifen, die sich nicht länger im Stack des Threads befinden.
Dadurch würd es zu einem Speicherzugriffsfehler kommen, da die Variable, welche im Thread erzeugt wurde, nach dem Ende des Threads nicht länger existiert.
Damit wären wir auch schon am Ende dieses Tutorials.
MfG + HF
TPM
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|
|