Threadsicherheit

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von MasterQ.

    Threadsicherheit

    Hallole,

    ​ja, Threadsicherheit. Ich bin da noch nicht so firm drin und verdreh mir eher das Hirn als dass ich da sicher wäre wie ich das angehen soll.


    ​Ich habe mir eine Klasse gebastelt, die im Prinzip eine List(of String) verwaltet. Es gibt neben anderen eine Methode add, die Strings hinten anhängt und eine Methode read, die den ersten Eintrag zurückgibt und diesen aus der Liste entfernt.

    Dann gibt es einen Thread, der die Liste füllt. Alle 500ms gibt es einen neuen Eintrag hinten dran.

    ​Ein zweiter Thread holt alle 1000ms mit read alle Einträge ab und leert somit die Liste wieder.

    Normalerweise klappt das auch ohne besondere Vorsichtsmaßnahmen aber in seltenen und unglücklichen Zusammenhängen kommen sich die beiden Threads in die Quere. Das musste ich gestern leider feststellen. Also aufbohren und threadsicher machen. Nur wo setze ich an?

    Die Beispiele die man im Netz finden kann, beziehen sich sehr oft auf Steuerelemente (Controls). Die sind ja per se mit den entsprechenden Werkzeugen (invoke) ausgestattet. Und die anderen Beispiele beziehen sich in der Regel auf den Fall, dass zwei Threads jeweils die gleiche Methode aufrufen. Bei mir ist das aber etwas anders, da zwei Threads jeweils andere Methoden (add bzw. read) aufrufen.

    Im Moment tu ich mir da schwer und weiß nicht so recht wie recht wie ich es angehen soll. Hatte bisher noch keinen Bedarf. Wenn mir da jemand auf die Sprünge helfen könnte, wäre das super.

    Gruß

    MQ
    ScheduleLib 0.0.1.0
    Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „fichz“ ()

    @MasterQ Vielleicht genügt schon ein SyncLock docs.microsoft.com/de-de/dotne…ements/synclock-statement
    dass die Zugriffe voneinander getrennt werden.
    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!
    @fichz

    Danke für die Hinweise.

    Leider gestaltet es sich etwas komplizierter. Ich war vielleicht auch etwas missverständlich in meinem ersten Post.

    Eine BlockingCollection kann ich nicht verwenden, da diese keinen Zugriff auf ein Element beliebigen Indexes (Integer) zulässt. Die Take or TryTake-Methode gibt es nicht in der Variante mit einem Index. ​Eine Queue kann ich aus ähnlichen Gründen nicht verwenden. Ich bin mir auch nicht sicher, ob solche Methoden wie Last und ElementAt threadsicher sind und nicht nur TryTake oder TryDequeue.

    ​Meine eigene Liste macht schon mehr als eben nur ein Element hinten anhängen und vorne eins wegnehmen. So muss ich Elemente der "Liste" nachträglich bearbeiten können. Ich brauche also sowas wie

    VB.NET-Quellcode

    1. Listenelement(13)="Humbatäterä"
    bzw.

    VB.NET-Quellcode

    1. Listenelement(13)=Listenelement(13) & "Anhängsel"


    Mein Liste verhält sich nach außen ähnlich einer List(Of String) ist aber eigentlich ein Array mit einem Überbau.


    @RodFromGermany

    ​An sowas dachte ich zunächst auch. Dieses SyncLock bezieht sich aber auf einen Codeabschnitt und verhindert, dass zwei Threads gleichzeitig diesen ausführen.

    ​Ich muss aber verhindern, dass der eine Thread nicht gerade ausliest, wenn der andere schreibt oder umgekehrt. Beide Threads greifen über unterschiedlichen Code auf eine gemeinsame Datenstruktur zu. Ich muss also eine Datenstruktur blocken und keinen Code.
    @MasterQ Du musst beide Code-Stellen mit demselben Object blocken.
    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!
    Das beschriebene Problem heißt Producer-Consumer-Problem und ist eine Standardaufgabe in der Informatik. Das heißt auch, dass es eine Standardlösung dafür gibt. Deren Implementierung (BlockingCollection) hat @fichz schon in #2 zitiert. Üblicherweise dient sie dazu, den Datenfluss zwischen zwei Threads zu synchronisieren, sodass der eine ggf. auf den anderen wartet.

    Edit: Weitere Anforderungen (z.B. beliebiger Zugriff auf Indizes) sind in dieser Variante nicht vorgesehen - die müsstest du selbst implementieren. Weißt du denn im Voraus, welchen Index du wann brauchst? Oder ist das komplett zufällig? Mir erscheint das ganze Vorhaben zudem ein wenig seltsam - wieso musst Du Elemente nach dem Hinzufügen ändern? So wie ich das verstehe, gibt es also 2 Threads und 3 Operationen: read element, write element und edit element. Gibt es noch weitere?
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „hal2000“ ()

    Mir ist bewusst, dass mein Problem kein echtes Problem ist sondern eher eine Standardsituation, zumindest was die Threadsicherheit angeht. Mir ist auch bewusst, dass ich da noch Wissensdefizite habe.

    ​Für den hier genannten Fall verwende ich eine selbstgebastelte Klasse, die ich auch an anderer Stelle (sowohl in der Anwendung die den Anlass für meine Post hier ist, als auch andere) nutze. Wenn ich jetzt dran rumdrehe, muss ich sicherstellen, dass ich mir nix an anderer Stelle kaputt mache. Wenn ich es nicht hinbekomme, dass ich meine Klasse so allgemein halten kann wie es bisher war, muss ich für den speziellen Fall wo die Threadsicherheit wichtig wird, eine andere Lösung anstreben. Dann ist der Code dort umzuschreiben. Meine erste Idee des geringsten Aufwandes war, meine Klasse mit ein paar Zeilen so zu modifizieren, dass der Rest bleiben kann. Wenn das nicht geht, geht die Welt nicht unter aber ich muss mehr Aufwand treiben bzw. das Konzept an der kritischen Stelle überdenken.

    Ob nu BlockingCollection oder ConcurrentQueue die bessere Lösung ist, das muss ich jetzt checken.

    ​Dazu meine Frage, ob alle Methoden der beiden sicher sind oder nur die Try-Versionen. Aus der Beschreibung geht das nicht eindeutig hervor. Wenn nämlich Add/Take oder Enqueue/Dequeue threadsicher wären, warum bräuchte man noch die Try?

    MasterQ schrieb:

    muss ich sicherstellen, dass ich mir nix an anderer Stelle kaputt mache.
    Fang an mit einer Sicherungskopie.
    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!

    MasterQ schrieb:

    Concurrent-Dinger
    Da musst Du mal die Microsoft-Docs befragen, das ist wahrscheinlich schneller geklärt als eine Frage hier im Forum.
    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!
    Alle Methoden der genannten Klassen sind threadsicher. Siehe dazu: docs.microsoft.com/en-us/dotne…ections/thread-safe/index. Früher stand das mal in der Doku zu jeder Methode, hier offenbar nicht mehr.

    MasterQ schrieb:

    Wenn ich jetzt dran rumdrehe, muss ich sicherstellen, dass ich mir nix an anderer Stelle kaputt mache.

    Unit Tests! Die laufen dann nach jedem Build durch und überprüfen, ob sich die Klasse (das Testobjekt) nach außen immer gleich verhält. Wenn einer scheitert, weißt du, wo der Fehler liegt.

    MasterQ schrieb:

    ein paar Zeilen so zu modifizieren, dass der Rest bleiben kann

    Bestehenden Code nachträglich threadsicher zu machen ist keine gute Idee. Das führt fast immer zu Problemen, sodass du gleich den gesamten Code neu schreiben kannst. Die Quick&Dirty-Lösung für Threadsicherheit ist, dass du den Inhalt jeder Methode deiner Klasse in einen SyncLock-Block packst. Da geht auf jeden Fall nichts schief, aber parallel läuft dann natürlich auch nichts mehr, was den Multithread-Ansatz ad absurdum führt.
    Gruß
    hal2000

    hal2000 schrieb:

    Alle Methoden der genannten Klassen sind threadsicher. Siehe dazu: docs.microsoft.com/en-us/dotne…ections/thread-safe/index. Früher stand das mal in der Doku zu jeder Methode, hier offenbar nicht mehr.


    Den Artikel hatte ich gelesen. Na ja, eindeutig ist der nicht. Daher meine Unsicherheit, ob die Threadsicherheit für alle Methoden gilt.




    MasterQ schrieb:

    Wenn ich jetzt dran rumdrehe, muss ich sicherstellen, dass ich mir nix an anderer Stelle kaputt mache.

    Unit Tests! Die laufen dann nach jedem Build durch und überprüfen, ob sich die Klasse (das Testobjekt) nach außen immer gleich verhält. Wenn einer scheitert, weißt du, wo der Fehler liegt.

    OK, schau ich mir mal an.