Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

Threading-Optimierung

von MitgliedThePuppetMasterSeite 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.


 

Gehe zu Seite Gehe zu Seite  1  2  3  4  5  6  
Zusätzliche Informationen und Funktionen
  Bearbeiten Bearbeiten  

  Versionen Versionen