Meine HighScore-File soll vor gleichzeitigem Benutzen gesichert werden! Wie?

  • C#

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von nogood.

    Meine HighScore-File soll vor gleichzeitigem Benutzen gesichert werden! Wie?

    Hi,

    ich möchte eine Highscore in meiner Webseiten-App benutzen. Der User erledigt Aufgaben, die Zeit die er dafür braucht wird gestoppt und die 5 schnellsten Zeiten sollen in einer Highscoreliste angezeigt werden.
    Geht auch alles, ABER ... ich hab Schwierigkeiten die Daten zu Persistieren.

    1. Lösungsidee: War die Highscore per Dependency Injection AddSingelton einfach für so lange wir der Server die App laufen hat zu speichern und so jedem Besucher die Werte anzeigen zu können.
    Problem: Meine Webhoster schaltet die App nach ca. 20 Minuten ohne Seiten Aktivität von Benutzern aus. Das heiß der Speicher hat bei einem erneuten Aufruf wieder die Default Highscorewerte :(.

    2. Lösungsidee: Ich müsste eine Datenbank anknüpfen.
    Problem: Da hab ich zwar eine kostenlose Lösung, bei der gibt es aber Zugriffslimits (1000 Zugriffe pro Tag oder so). Auch wenn das Limit wohl locker reichen würde. Lieber wäre mir eine andere Lösung.

    3. Lösungsidee: Lokal auf dem Server die Daten in einer .json speichern.
    Problem: Wie mache ich es, dass es nicht zu Schwierigkeiten kommt bei gleichzeitigem Lesen/Schreiben der Datei? Ich hab rumgegoogelt und dachte das ReadWriteLockSlim die richtige Class wäre, um das zu machen. Bekomme das aber nicht hin.

    Die Highscore Daten würden ja häufig gelesen werden und nur selten geschrieben werden. Daher dachte ich das das mit einer Datei doch gehen könnte.

    Mein Code fürs Schreiben sieht so aus:

    C#-Quellcode

    1. public async Task SaveHsFile(PlayerPoco player)
    2. {
    3. using ReaderWriterLockSlim myWriteLock = new();
    4. myWriteLock.EnterWriteLock();
    5. try
    6. {
    7. var jsonString = JsonSerializer.Serialize<List<HighScoreEntry>>(player.GameHsList);
    8. await File.WriteAllTextAsync(player.GameHsFileName, jsonString);
    9. }
    10. finally
    11. {
    12. myWriteLock.ExitWriteLock();
    13. }
    14. }


    Der Fehler den ich bekomme sieht so aus.

    Ich starte lokal die App aus VisualStudio alles funktioniert, auch das Speichern/Lesen der Highscore Datei. ABER wenn ich dann ein zweites mal 'spiele' den Browser geöffnet lasse und erneut alles wie beim ersten mal mache bekomme ich folgende Fehlermeldung im Browser:
    [2021-11-19T21:25:27.513Z] Error: System.Threading.SynchronizationLockException: The write lock is being released without being held. at System.Threading.ReaderWriterLockSlim.ExitWriteLock()
    Mit Verweiss auf die Zeile 12. in meinem Code.

    Im gesamten Code ist das die einzige Stelle wo ich ReadWriteLockSlim benutze.

    Jemand eine Idee was ich falsch mache, oder einen besseren Lösungsansatz? Danke ... LG nogood
    codewars.com Rank: 4 kyu
    @ErfinderDesRades Die Syntax von using wurde geändert ich glaube C# 8 (mir wird das als VS-Autokorrektur-VerbesserungsVorschlag-Dings angezeigt).

    Ich hab das Slim-Ding auch noch nie benutzt, ich dachte das File wäre solange 'geblockt' bis ich es wieder im finally block freigebe.

    Ich hab meinen Code mal so verändert if (myWriteLock.IsWriteLockHeld) :
    Spoiler anzeigen

    C#-Quellcode

    1. public async Task SaveHsFile(PlayerPoco player)
    2. {
    3. using ReaderWriterLockSlim myWriteLock = new();
    4. myWriteLock.EnterWriteLock();
    5. try
    6. {
    7. var jsonString = JsonSerializer.Serialize<List<HighScoreEntry>>(player.GameHsList);
    8. await File.WriteAllTextAsync(player.GameHsFileName, jsonString);
    9. }
    10. finally
    11. {
    12. if (myWriteLock.IsWriteLockHeld)
    13. {
    14. myWriteLock.ExitWriteLock();
    15. }
    16. }
    17. }


    Bis jetzt noch keinen Fehler gehabt. Vielleicht wars es das schon ?!

    ----
    Ich hab es jetzt auch nochmal als Klassenvariable gemacht geht bis lang auch ohne Fehler hmmmm.

    Spoiler anzeigen

    C#-Quellcode

    1. private readonly ReaderWriterLockSlim myWriteLock = new ();
    2. public async Task SaveHsFile(PlayerPoco player)
    3. {
    4. myWriteLock.EnterWriteLock();
    5. try
    6. {
    7. var jsonString = JsonSerializer.Serialize<List<HighScoreEntry>>(player.GameHsList);
    8. await File.WriteAllTextAsync(player.GameHsFileName, jsonString);
    9. }
    10. finally
    11. {
    12. if (myWriteLock.IsWriteLockHeld)
    13. {
    14. myWriteLock.ExitWriteLock();
    15. }
    16. }
    17. myWriteLock.Dispose();
    18. }​
    codewars.com Rank: 4 kyu

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

    naja - vermutlich ist myWriteLock.IsWriteLockHeld immer false, und myWriteLock.ExitWriteLock() wird dann nie aufgerufen - prima!

    Nur glaub dann könnteste den myWriteLock auch ganz weglassen, und würdest genausoviele oder wenig Fehler bekommen.

    Jdfs. eine KlassenVariable in einer Methode zu disposen, die vielfach aufgerufen wird - denk da nochmal drüber nach.
    Hmm ... ich weiß noch nicht mal, ob das was ich mache überhaupt nötig/richtig ist. Daher ja auch hier die Frage an erfahrenere Programmierer.

    Die Methode die oben im ersten Post steht, ist ein Teil einer Klasse, die als Singelton im Projekt bereit steht (siehe da den Punkt 1. /oben im ersten Post).
    Meinem Verstehen nach bedeutet das, dass dies die einzige Instanz ist die es gibt und alle User sich diese Instanz teilen. Nun weiß ich nicht was ich für Vorkehrungen treffen muss, damit die Files auf der HD nicht irgendwann durch gleichzeitiges öffnen/schließen/lesen/schreiben 'kaputt' gehen. Oder kann das so gar nicht passieren? Und jeder User Befehl wird der Reihe nach abgearbeitet - geschrieben und gelesen.

    Server -Singelton Methode mit Save und Load

    User A, B, C, D,... haben auf Ihren Endgeräten die App laufen und drücken irgendwann auf einen Button und wollen die Daten lesen/schreiben und benutzen dazu immer die Methoden des Singelton-Services.

    Lg nogood
    codewars.com Rank: 4 kyu

    nogood schrieb:

    Meinem Verstehen nach bedeutet das, dass dies die einzige Instanz ist die es gibt und alle User sich diese Instanz teilen.
    Das wär, als ob Du sagst, dass alle Fahrgäste eines Busses automatisch mit dem selben Busfahrer unterwegs sind. Sie benutzen den selben Bus. Aber ob es die ganze Fahrt über derselbe Busfahrer ist, kann das Busunternehmen ja selber festlegen. Nach ner bestimmten Strecke könnte ja auch ein anderer Fahrer einspringen.
    Singleton heißt, dass es zur Programmlaufzeit nur eine Instanz der Klasse gibt. Dass die User dieselbe Instanz nutzen, liegt aber daran, dass alle User dieselbe App ansteuern, also mit ihr kommunizieren. Aber das Singleton selber ist effektiv von der Nutzerzahl entkoppelt. Die User teilen sich also keine Klassen-Instanz, sie kommunizieren nur mit derselben App-Instanz. Das Resultat ist hier dasselbe. Aber die Grundannahme ist anders. Aber vielleicht hattest Du auch das eigentlich so gemeint.

    nogood schrieb:

    Nun weiß ich nicht was ich für Vorkehrungen treffen muss, damit die Files auf der HD nicht irgendwann durch gleichzeitiges öffnen/schließen/lesen/schreiben 'kaputt' gehen.
    Das Schlimmste, was m.E. passieren kann, ist, dass es eine IO-Exception gibt, weil auf die Datei nicht zugegriffen werden kann.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Das Schlimmste, was m.E. passieren kann, ist, dass es eine IO-Exception gibt, weil auf die Datei nicht zugegriffen werden kann.

    Wie sollte ich dieses Problem am besten lösen.

    1. For-Schleife mit z.B. 5 Wiederholungen inkl. Wait-200ms und dort drinnen dann Try-Catch mit return ganz raus wenn try funktioniert hat?

    2. @VaporiZed Kurz zu deiner Analogie Singelton. Also Klasse = Bauplan für einen Bus // Instanz = ein echter Bus gebaut nach dem Bauplan // Singelton = Versprechen es gibt nur einen Bus. Ist das dann so wie eine static-Class oder wo unterscheiden sich die zwei Dinge grundlegend (ganz davon ab, dass ich nicht weiß was der Busfahrer sein soll (Busfahrer = Inhalt/Zustand der Variablen in der Klasse) // falls das überhaupt wichtig ist in dem Beispiel)?

    Lg nogood
    codewars.com Rank: 4 kyu
    Hallo,
    anstelle einer JSON-Datei, kannst du vielleicht mit SQLite arbeiten. Die Datenbank ist letztlich eine einfache Datei um die "Vorzüge" einer Datenbank erweitert, heißt auch, dass Mutli-User-Zugriff unterstützt wird. Es gibt für .NET Provider, um auf SQLite-Datenbanken zugreifen zu können. Evtl. ist das eine Option für dich.
    @ISliceUrPanties Jo sieht nach einer guten Lösung aus. Ich wusste nicht, dass es das Konzept SQLite gibt. Ich war immer unter der Annahme, dass ich einen DB-Server brauche. DANKE.
    ----
    Ich tu mich leider immer noch schwer mit dem ganzen Thema DB (Syntax, Extra Viewer, Tabels etc.). Daher hab ich mit dem Wissen 'no DB Server needed' nochmal gegoogled und bin jetzt auf LiteDB (openSource) gestoßen, quasi die SQLite Variante für noSQL-DBs. Unter Winforms (.NET 6.0) hab ich es zum laufen gebracht.
    Das heißt hoffentlich, dass es auch mit Blazor lauft?
    codewars.com Rank: 4 kyu
    Den For-Schleifen-Ansatz hab ich tatsächlich so seit längerem bei einem Programm in Benutzung, welches mir Fehlerdaten aus einer App ins Dateisystem schreibt. Diese App ist von mehreren Usern in Benutzung.
    In meiner Analogie (ich weiß, Vergleiche haben gebrochene Beine und hinken deshalb) wär das Singleton nur die Tatsache, dass man das Reiseziel einem Busfahrer* sagt und dafür gesorgt wird, dass nicht verschiedene Busfahrer von verschiedenen Fahrgästen unterschiedliche Wunschziele bekommen und die Busfahrer dann versuchen, gleichzeitig an unterschiedliche Ziele zu fahren. Also: Nur ein (aktiver) Fahrer, der allein die Route festlegt.

    * also kein Linienbus, sondern eher ein Reisebus mit mehreren Ersatzbusfahrern und grober Reisetour, bei dem sich die Fahrgäste Reisetourwunschzielorte aussuchen können ;)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.