In Extra Thread aus Klassen nur lesen.. kann es zu Problemen kommen?

  • VB.NET

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    In Extra Thread aus Klassen nur lesen.. kann es zu Problemen kommen?

    Hi,

    mal eine kleine Frage.

    Wenn ich 3 Klassen habe.
    1 Formklasse (oder ähnliches), 1 Settingsklasse und eine Klasse die irgendwas in einem Thread ausführt.
    Die Threadklasse bekommt eine Referenz auf die Settingsklasse mit.
    Änderungen in der Settingsklasse erfolgen ausschliesslich zB über die Formklasse mit nem SyncLock.

    Meine Frage:
    Kann es irgendwie zu Problemen kommen, wenn ich aus dem Thread lesend auf die Properties der Settingsklasse zugreife, diese aber nicht ändere, ohne Synclock?
    M.E. sollte ein rein lesender Zugriff zu keinerlei Race Bedingung oder ähnlichem führen?
    Ist das unsauber? Sollte man generell, egal ob lesend/schreibend, SyncLock beim Zugriff verwenden?

    Danke für jeden Input.

    Gruss Mono
    Das ist meine Signatur und sie wird wunderbar sein!
    Wenn die GUI schreibend und der Thread lesend gleichzeitig zugreifen, kann es eng werden, dies tritt aber nur bei echter Parallelverarbeitung auf (MultiCore).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Imo ist das immer unkalkulierbar.
    Wenn das Gui eine Änderung an den Settings vornimmt, hast du keine Ahnung, ab welchem Zeitpunkt diese Änderung auch im Nebenthread wirksam ist.
    Du mußt also davon ausgehen, dass der Nebenthread lustig mit den falschen Setting-Werten erstmal weiter-arbeitet.
    Hi
    SyncLock sorgt dafür, dass der Code-Abschnitt im SyncLock selber nur auf einem Thread ausgeführt wird. Wenn du den Zugriff auf Objekte synchronisieren möchtest, kannst du System::Threading::Monitor verwenden. Es gibt auch noch andere Synchronisationsmechanismen, aber dieser scheint hier je nach Veerwendung angebracht.
    Beim Threading wird jedem Thread eine bestimmte Zeit zum Arbeiten gegeben. D.h. Verhalten sichern, wo es sinnvoll ist.

    Gruß
    ~blaze~

    ~blaze~ schrieb:

    SyncLock sorgt dafür, dass der Code-Abschnitt im SyncLock selber nur auf einem Thread ausgeführt wird.

    ziemlich ungenau.

    Synclock macht aus einem beliebigen Objekt eine Thread-Sperre.
    Solange ein Lock auf dem Objekt ist, können andere Threads keinen Synclock-Bereich betreten, der dasselbe Objekt lockt.

    Also wenn du ein Objekt lockst und dann einen Schreibvorgang auf eine Liste ausführst, dann kannst du an anderer Stelle was zum Lesen aus der Liste coden, und das in einen Synclock-Block tun, der dasselbe Objekt lockt.
    Ergebnis: wenn geschrieben wird warten lesende Threads solange, und anschließend sind die aktuellen Daten verfügbar.

    @blaze: was macht Monitor im Gegensatz zu Synclock?

    Mono schrieb:

    Kann es irgendwie zu Problemen kommen

    Ja. Das hängt aber vom Typ der Property ab bzw vom Code der in der Property-Methode steht.

    Bsp.:
    If Dic.ContainsKey("foo") then Return Dic("foo")

    sieht trivial aus, aber was, wenn genau VOR dem Return, aber nach dem .Contains genau dieses Element aus dem Dictionary gelöscht wird? Beides sind rein lesende Vorgänge, aber nichts stellt sicher, dass zwischen den beiden Vorgängen kein anderer Thread läuft, der schreibend/löschend ist.
    Mir ist prinzipiell die Verwendung von Synclock oder Monitor.Enter klar.

    Die Properties sind alle primitive Datentypen wie String/Integer und werden quasi nur returned. Sind also eigentlich nur Fields mit nem Property gekapselt. Ohne zusätzliche Prüfungen.
    Bei Tests auch mit mehreren Threads hatte ich nie Probleme mit rein lesendem Zugriff.

    Du mußt also davon ausgehen, dass der Nebenthread lustig mit den falschen Setting-Werten erstmal weiter-arbeitet.


    Das ist mir bewusst und macht in diesem Fall nichts.
    Es geht nur um die Sauberkeit und das Verständnis.
    Kann paralleles lesen irgendwie zu einer Exception oder ähnlichem führen?
    Das ist meine Signatur und sie wird wunderbar sein!
    Zu Exceptions nicht, aber zu falschen Ergebnissen (es sei denn der Wert wird nur am Anfang einmal geschrieben und dann wirklich nur noch gelesen).

    Der Monitor bzw. SyncLock gehört in die Settings-Klasse und bekommt ein privates Lock-Objekt, damit jeder, der auf irgendwelche Eigenschaften zugreift, vom gleichen Lock blockiert wird.

    Falsch:

    VB.NET-Quellcode

    1. Class A
    2. SyncLock x
    3. 'write to MyProperty in Settings
    4. End SyncLock
    5. End Class
    6. Class B
    7. SyncLock y
    8. 'read from MyProperty in Settings
    9. End SyncLock
    10. End Class
    11. Class Settings
    12. Property MyProperty [...]
    13. End Class


    Richtig:

    VB.NET-Quellcode

    1. Class A
    2. 'write to MyProperty in Settings
    3. End Class
    4. Class B
    5. 'read from MyProperty in Settings
    6. End Class
    7. Class Settings
    8. Private x [...]
    9. Property MyProperty [...]
    10. Get / Set
    11. SyncLock x
    12. Return [...] / [...] = value
    13. End SyncLock
    14. End Get / Set
    15. End Property
    16. End Class


    Mit mehreren Locks kannst du die Properties voneinander unabhängig machen. Wenn ein Wert niemals von verschiedenen Thread gebraucht wird, kann er ein eigenes Lock bekommen, sodass andere Threads (in anderen Properties) nicht blockiert werden. Wird aber schnell kompliziert. Vielleicht auch noch interessant für dich: Such mal nach der Klasse "ReaderWriterLock".
    Gruß
    hal2000
    Wie im Beispiel "Richtig" habe ich es in Verwendung.
    Jedoch habe ich den Lock nur bei Set und nicht bei get.

    Habe mir aber mal den ReaderWriterLock angeschaut:

    VB.NET-Quellcode

    1. Try
    2. rwl.AcquireReaderLock(timeOut)
    3. Try
    4. ' It is safe for this thread to read from
    5. ' the shared resource.
    6. Display("reads resource value " & resource)
    7. Interlocked.Increment(reads)
    8. Finally
    9. ' Ensure that the lock is released.
    10. rwl.ReleaseReaderLock()
    11. End Try
    12. Catch ex As ApplicationException
    13. ' The reader lock request timed out.
    14. Interlocked.Increment(readerTimeouts)
    15. End Try


    Das ist so ein Beispiel aus MSDN. Scheint besser als Monitor und Synclock zu sein, da AcquireReaderLock keinen exklusiven Zugriff für einen Thread bereitstellt, sondern einen geteilten Zugriff für mehrere Reader.
    Also genau das, was ich mir vorstelle.

    Und da das in letzter Zeit häufig ein Thema war. Wie könnte man hier ohne Try Catch arbeiten? :D

    Ciao und thx für die Vorschläge
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Wie könnte man hier ohne Try Catch arbeiten?


    a) Wozu
    ->
    Das Acquire KÖNNTE schiefgehen. Was soll dann passieren? Anwendungsabsturz? Heulen und Zähnklappern beim Anwender? Oder ne MessageBox die den User auffordert es noch mal zu probieren oder alternativ seine Daten zu speichern und das Programm neu zu starten?

    b) Wieso zwei Try-Catch?
    ->
    try
    rwl.AcquireReaderLock(timeOut)
    ...
    finally
    if rwl.isreaderlockheld then rwl.releasereaderlock
    Das Beispiel ist 1 zu 1 aus MSDN.
    Ich sage auch nicht, dass ich der Meinung bin, hier sollte kein Try Catch rein.
    Ich erinner mich nur an Diskussion deren eine Seite eine Argumentation hatte, die ungefähr lautet:
    Try Catch kann man eigentlich immer vermeiden :p

    Heulen und Zähnklappern beim Anwend(ungsentwickl)er?

    Die Gefahr besteht schon :)
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Ich erinner mich nur an Diskussion deren eine Seite eine Argumentation hatte,

    Diese Argumentation gibt es und in Bezug darauf zitiere ich mal aus "Zeit des Erwachens": Wenn Sie recht hätten, würde ich Ihnen zustimmen

    Wie man sieht ist die hier geworfene Exception eine "Nachricht". Nachrichten hat man zu verarbeiten!
    In C/C++ sähe die Signatur vermutlich SO aus:
    public int AcquireReaderLock(int timeout)
    Wenn alles OK, gibt die Funktion 0 zurück, ansonsten einen Int, der den Fehler näher beschreibt.
    Typischer Aufruf in C/C++:
    if (int err = AcquireReaderLock(int timeout))
    {
    // locking failed, wtf? Lets inform user and then go on
    }

    Eventuelle Syntaxfehler mögen der Tatsache geschuldet sein, dass ich halt KEIN native-born-c'ler bin ;)
    Da es hier ja um den Zugriff auf Properties geht muss dennoch eine sinnvolle Fehlerbehandlung her.
    Im Catch Zweig ein Nothing zurückzugeben und die Property vor Anwendung auf selbiges zu prüfen scheint mir ein bissl dirty.
    Alternativ könnte man eine eigene Exception werfen.

    VB.NET-Quellcode

    1. Private _lPath As String
    2. ''' <summary>
    3. ''' Der Ordner zu den Log-Dateien.
    4. ''' </summary>
    5. ''' <value></value>
    6. ''' <returns></returns>
    7. ''' <remarks></remarks>
    8. Public Property LogPath() As String
    9. Get
    10. Try
    11. rwl.AcquireReaderLock(Me.LockTimeOut)
    12. Return _lPath
    13. 'Timeout läuft ab, bevor die Sperranforderung erteilt wird
    14. Catch ex As ApplicationException
    15. Return Nothing
    16. Finally
    17. If rwl.IsReaderLockHeld Then rwl.ReleaseReaderLock()
    18. End Try
    19. End Get
    20. Set(ByVal value As String)
    21. Try
    22. rwl.AcquireWriterLock(Me.LockTimeOut)
    23. _lPath = value
    24. 'Timeout abgelaufen, Property kann nicht gesetzt werden
    25. Catch ex As ApplicationException
    26. Throw New LockException
    27. Finally
    28. If rwl.IsWriterLockHeld Then rwl.ReleaseWriterLock()
    29. End Try
    30. End Set
    31. End Property
    Das ist meine Signatur und sie wird wunderbar sein!
    guck - ReaderWriteLock kannte ich noch garnet - scheint mir eine prima Lösung der SyncLock/Monitor - Unschönheit, dass gleichzeitige Lesevorgänge sich eigentlich unnötigerweise gegenseitig behindern, während doch nur sichergestellt sein muß, dass ein Schreibvorgang alle anneren Lese- und Schreib-Vorgänge warten lassen muß.

    TryCatch kann man nicht immer vermeiden - imo sollmans vermeiden, wenn mans vermeiden kann, aber das geht halt nicht immer. gugge vlt. TryCatch ist ein heißes Eisen

    Dieses ReaderLockWriter-Dings scheint numal so designed, dasses die TimeOut-Meldung via Exception mitteilt, und das ist ja auch ganz sinnvoll.
    Olle SyncLock/Monitor sieht ja gar keinen TimeOut vor, sodasses in komplizierteren Fällen auch zu Situationen kommen kann, wo zwei Threads gegenseitig aufeinander warten, und die Anwendung steht still: Diese Form von Absturz heißt "DeadLock".

    Mittm ReaderWriterLock fliegt dann iwann (nämlich genau nachm TimeOut) eine Exception, und dassis natürlich ein enormer Vorzug gegenüber einem DeadLock, bei dem ja garnix mehr geht.
    Und hier im Demo besteht die ExceptionBehandlung halt in der Ausgabe der Exception - die wollen das Feature ja demonstrieren.

    @Mono: Dein Sample findichjetz bisserl überkandidelt. Du willst doch nur einen neuen LogPath setzen.
    Das würdeja nur zu einem Timeout führen können (keinesfalls zu einem Deadlock), wenn da bereits sekundenenlang herumgeloggt würde in deim LogPath, und zwar ein einziger Log-Vorgang - ist das realistisch?
    Und da hättest du dann ein ganz besonderes Problem mitte Fehlerbehandlung: Wie logge ich einen Fehler, wenn ich den Pfad zur Log-Datei nicht setzen kann?
    Das annere Problem ist, wenn wirklich sekundenlange Logs geschrieben werden - dann dürfte die Datei ziemlich bald Gigabyte groß werden.

    Tatsächlich denke ich aber, der Logpfad wird zum StartUp ein einziges Mal gesetzt, und dann vlt. nochmal, wenn einer die Optionen entsprechend ändert. Und ein Log-Aufruf schreibt vlt. 200 Bytes.

    lange rede kurzer sinn: Beim Loggen denke ich, dass ReaderWriterLock nicht das geeignete Mittel ist, denn es ist ein Mittel für viele Lese-Vorgänge und gelegentliche Schreiber.
    Logging ist aber eiglich nur schreiben, und da ist SyncLock imo ganz i.O..

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Dieses ReaderLockWriter-Dings scheint numal so designed, dasses die TimeOut-Meldung via Exception mitteilt,

    Kleiner Tip: DAS ist genau das Verhalten für das MS die Exception VORGESEHEN hat. MS hat sich nämlich VORSÄTZLICH gegen das (von mir oben aufgezeigte) "Fehlerschema" aus C/C++ entschieden.
    Googles Go macht hat es da einfacher, denn da können Funktionen ihren "Wert" UND gleichzeitig (!) einen Fehler zurückmelden.
    Ja, diese Property ist ein schlechtes Beispiel.
    Die Properties die es betrifft sind eher sowas hier:

    TimeOut As Integer
    LogLevel As LogLevelEnum
    Compression as Boolean

    Diese kann man zur Laufzeit ändern und werden daher immer wieder gelesen (und sollen eben nicht einmal am Anfang festgelegt werden).
    Das ist meine Signatur und sie wird wunderbar sein!
    sehe immer noch keine Verwendung für einen TimeOut.

    auch könnteman die Methode so gestalten, dass die Werte als Argument mitgegeben werden.
    Zumindest beim LogLevel fände ich eigentümlich, wenn der nicht zusammen mit dem MeldeText geliefert würde, sondern wenn da immer vor einem Log-Aufruf noch ein Level extra zu setzen wäre.
    Das LogLevel wird nur von einem (Haupt) Thread gesetzt.
    Die laufenden Asynchronen Operation/Threads fragen das LogLevel nur ab und loggen dementsprechend über eine eigene Logfunktion.
    Genauso mit den beiden anderen Properties.

    Das TimeOut is ggf. unsinnig, vor allem im AcquireWriterLock.
    Du meinst also, das TimeOut generell weglassen bzw auf infinite setzen?
    Da die Schreib -und Lesevorgänge sowieso nicht länger dauern?
    Das ist meine Signatur und sie wird wunderbar sein!