Tutorial
Threading-Optimierung
von ThePuppetMaster | Seite 3 von 6 |
Häufig wird auch außer Acht gelassen, das verteilte Funktionen Probleme verursachen können, wenn diese nicht geschützt werden. Vor allem beim Zugriff auf LinkedLists ist dies von entscheidender Bedeutung.
Als Beispiel nutzen wir ein kleines Programm (nicht optimiert), um eine LinkedList zu erstellen
'Ein Typ definieren
Type Bla
V_Next as Bla Ptr
V_Prev as Bla Ptr
V_Data as UInteger
End Type
Dim Shared LinkedList_First as Bla Ptr
Dim Shared LinkedList_Last as Bla Ptr
'Funktion welche einen neuen Eintrag in die LinkedList erzeugt und diesen neuen Eintrag als Rückgabe liefert.
Function LL_Add(V_Value as UInteger) as Bla Ptr
If LinkedList_Last 0 Then
LinkedList_Last->V_Next = CAllocate(SizeOf(Bla))
LinkedList_Last->V_Next->V_Prev = LinkedList_Last
LinkedList_Last = LinkedList_Last->V_Next
Else
LinkedList_Last = CAllocate(SizeOf(Bla))
LinkedList_First = LinkedList_Last
End If
LinkedList_Last->V_Data = V_Value
Return LinkedList_Last
End Function
'Die Thread Sub.
Sub Thread()
Dim TPtr as Bla Ptr
For X as UInteger = 1 to 10
TPtr = LL_Add(X) 'Eintrag erzeugen
TPtr->V_Data += 10 'Wert im neuen Eintrag um 10 erhöhen
Next
End Sub
'Hier würden einige Threads initialisiert werden.
'...
Wie sicher schon zu erkennen ist, fehlt das Mutex. In gängigen Fällen würde man die LL_Add Funktion mit einem MutexLock und Unlock ausstatten.
'...
Function LL_Add(V_Value as UInteger) as Bla Ptr
MutexLock(Mutex)
If LinkedList_Last 0 Then
LinkedList_Last->V_Next = CAllocate(SizeOf(Bla))
LinkedList_Last->V_Next->V_Prev = LinkedList_Last
LinkedList_Last = LinkedList_Last->V_Next
Else
LinkedList_Last = CAllocate(SizeOf(Bla))
LinkedList_First = LinkedList_Last
End If
LinkedList_Last->V_Data = V_Value
Function = LinkedList_Last
MutexLock(Mutex)
End Function
'...
Wir haben, um die Linkedlist weiterhin schützen zu können anstatt des Return das "Function"-Statement verwendet. An sich ist dieses Vorgehen sicher, wenn es da nicht einen kleinen Denkfehler gäbe.
Nachdem wir den Pointer per "Function" in den Stack gelegt haben, Unlocken wir das Mutex. Und hier liegt der eigentliche Fehler, denn die Funktion gibt uns jetzt im ungeschützten Zustand einen Pointer auf ein Element zurück.
Auf dieses Element greifen wir anschließend im Thread zu und ändern den Wert dieses Elements.
Da der Thread kein Mutex nutzt würde hier ein wechselseitiger Zugriff möglich werden.
Die einfachste Art dies zu verhindern stellt das hinzufügen eines MutexLock() dar.
'...
Sub Thread()
Dim TPtr as Bla Ptr
For X as UInteger = 1 to 10
TPtr = LL_Add(X) 'Eintrag erzeugen
MutexLock(Mutex)
TPtr->V_Data += 10 'Wert im neuen Eintrag um 10 erhöhen
MutexUnLock(Mutex)
Next
End Sub
'...
Sieht schön aus, ist jedoch nicht sicher!!! Man muss hier beachten, das zwischen dem "End Function" der "LL_Add" Funktion und dem ersten "MutexLock()" in der "Thread" Sub ein Bereich existiert, in dem der erzeugte Pointer auf das neue Element ungeschützt ist.
In dieser Teil kann ein anderer Thread auf die Linkedlist zugreifen und z.B. diesen gerade erzeugte Eintrag löschen. Damit würde der Pointer auf das neue Element ungültig.
Dieses Phänomen kann sehr simpel gelöst werden, indem man nicht beide Funktionen mit einem MutexLock() ausstattet, sondern nur die Prozedur, welche mit der Linkedlist oder diesem Speicherbereich arbeitet.
Dies gilt übrigens auch für Arrays!
'...
Function LL_Add(V_Value as UInteger) as Bla Ptr
If LinkedList_Last 0 Then
LinkedList_Last->V_Next = CAllocate(SizeOf(Bla))
LinkedList_Last->V_Next->V_Prev = LinkedList_Last
LinkedList_Last = LinkedList_Last->V_Next
Else
LinkedList_Last = CAllocate(SizeOf(Bla))
LinkedList_First = LinkedList_Last
End If
LinkedList_Last->V_Data = V_Value
Return LinkedList_Last
End Function
Sub Thread()
Dim TPtr as Bla Ptr
For X as UInteger = 1 to 10
MutexLock(Mutex)
TPtr = LL_Add(X) 'Eintrag erzeugen
TPtr->V_Data += 10 'Wert im neuen Eintrag um 10 erhöhen
MutexUnLock(Mutex)
Next
End Sub
'...
Es wird wieder das Return genutzt (spart auch 2 Programmzeilen im Quellcode). Anschließend schließen wir die Funktion "LL_Add" in das MutexLock() mit ein.
Dadurch sind alle Zugriffe auf die Linkedlist geschützt.
Natürlich muss hier grundsätzlich so vorgegangen werden. Soll z.B. ein Wert in der Linkedlist abgeändert werden, würde man sich praktischer Weise eine Funktion schreiben, welche das Entsprechende Element sucht um es später zu bearbeiten.
'Funktion zum suchen eines Eintrags in der LinkedList
Function LL_Get(V_Value as UInteger) as Bla Ptr
Dim TPtr as Bla Ptr = LinkedList_Last
Do Until TPtr = 0
If TPtr->V_Data = V_Value Then Return TPtr
TPtr = TPtr->V_Next
Loop
Return 0
End Function
Sub Thread()
Dim TPtr as Bla Ptr
'...
'Einträge hinzufügen...
'...
'Eintrag suchen und bearbeiten
MutexLock(Mutex)
TPtr = LL_Get(15)
If TPtr = 0 Then MutexUnLock(Mutex): Exit Sub
TPtr->V_Data += 10
MutexUnLock(Mutex)
End Sub
'...
Hier wird nach einem Element gesucht. Es müssen die selben Strukturierungen durchgeführt werden wie auch beim hinzufügen von Einträgen. Bedachte immer, das funktionsübergreifende Aufrufe auf gemeinsame Ressourcen zu wechselseitigen Problemen führen können.
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|
|