Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. States und Threadsicherheit

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    17
    1
    2.1k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    936

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.2k

States und Threadsicherheit

Geplant Angeheftet Gesperrt Verschoben JavaScript
3 Beiträge 2 Kommentatoren 139 Aufrufe 2 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • H Offline
    H Offline
    htz
    schrieb am zuletzt editiert von htz
    #1

    Hallo liebe ioBroker Community,
    ich nutze ioBroker und den JS-Controller schon seit einer ganzen Weile.

    Im Moment arbeite ich an einem globalen Service, der Nachrichten aus verschiedenen Modulen sammeln kann und diese bei Bedarf in eingehender Reihenfolge ausgibt.

    class NotificationService {
        private readonly stateDataId = 'javascript.0.NotificationServiceData';
    
        constructor() {
            if(!existsState(this.stateDataId)) {
                createState(this.stateDataId, JSON.stringify([]));
            }
        }
    
        public queueMessage = async(message: string) => {
            let queue = await this.loadState();
            log(`Got message to queue: "${message}"`);
            if(!this.hasMessage(message, queue)) {
               queue.push(message);
            } else {
                log(`NOT QUEUED: "${message}"`);
            }
            await this.persistState(queue);
        }
    
        public removeMessage = async(message: string) => {
            let queue = await this.loadState();
            let exists = this.hasMessage(message, queue);
            if(exists) {
                queue.splice(exists.idx, 1);
            }
            await this.persistState(queue);
        }
    
        public isEmpty = async() => {
            let queue = await this.loadState();
            return this.isEmptyQueue(queue);
        }
    
        private isEmptyQueue = (queue: string[]) => {
            return queue.length === 0;
        }
    
        public sendNext = async() => {
            let queue = await this.loadState();
            if(this.isEmptyQueue(queue)) {
                return;
            }
            log(`Queue length is ${queue.length}`);
            let message = queue.splice(0, 1)[0];
            log(`Message is: ${message}`);
            this.sendMessage(message);
            await this.persistState(queue);
        }
    
        private sendMessage = (message: string) => {
            // TODO
        }
    
        private hasMessage = (message: string, queue: string[]) => {
            log(`Queue is ${JSON.stringify(queue)}`);
            const idx = queue.indexOf(message);
            if(idx !== -1) {
                return {
                    idx: idx
                }
            }
            return false;
        }
    
        private persistState = async(queue: string[]) => {
            await setStateAsync(this.stateDataId, JSON.stringify(queue));
        }
    
        private loadState = async() => {
            let state = await getStateAsync(this.stateDataId);
            if(state.val) {
                log("State data is not null; parsing");
                return JSON.parse(state.val);
            }
            log(`State data is null, state is: ${JSON.stringify(state)}`);
            return [];
        }
    }
    

    Wenn ich den Service nun in unterschiedlichen Modulen benutze, dann kommt es vor, dass einzelne Nachrichten nicht in die Queue gelangen. Das scheint daran zu liegen, dass der State nicht Threadsicher ist.

    Gibt es hier eine Möglichkeit, um das Problem zu umgehen? Oder Nutze ich die States vielleicht einfach falsch? Welche Alternativen gibt es?

    Vielen Dank im Voraus!

    AsgothianA 1 Antwort Letzte Antwort
    0
    • H htz

      Hallo liebe ioBroker Community,
      ich nutze ioBroker und den JS-Controller schon seit einer ganzen Weile.

      Im Moment arbeite ich an einem globalen Service, der Nachrichten aus verschiedenen Modulen sammeln kann und diese bei Bedarf in eingehender Reihenfolge ausgibt.

      class NotificationService {
          private readonly stateDataId = 'javascript.0.NotificationServiceData';
      
          constructor() {
              if(!existsState(this.stateDataId)) {
                  createState(this.stateDataId, JSON.stringify([]));
              }
          }
      
          public queueMessage = async(message: string) => {
              let queue = await this.loadState();
              log(`Got message to queue: "${message}"`);
              if(!this.hasMessage(message, queue)) {
                 queue.push(message);
              } else {
                  log(`NOT QUEUED: "${message}"`);
              }
              await this.persistState(queue);
          }
      
          public removeMessage = async(message: string) => {
              let queue = await this.loadState();
              let exists = this.hasMessage(message, queue);
              if(exists) {
                  queue.splice(exists.idx, 1);
              }
              await this.persistState(queue);
          }
      
          public isEmpty = async() => {
              let queue = await this.loadState();
              return this.isEmptyQueue(queue);
          }
      
          private isEmptyQueue = (queue: string[]) => {
              return queue.length === 0;
          }
      
          public sendNext = async() => {
              let queue = await this.loadState();
              if(this.isEmptyQueue(queue)) {
                  return;
              }
              log(`Queue length is ${queue.length}`);
              let message = queue.splice(0, 1)[0];
              log(`Message is: ${message}`);
              this.sendMessage(message);
              await this.persistState(queue);
          }
      
          private sendMessage = (message: string) => {
              // TODO
          }
      
          private hasMessage = (message: string, queue: string[]) => {
              log(`Queue is ${JSON.stringify(queue)}`);
              const idx = queue.indexOf(message);
              if(idx !== -1) {
                  return {
                      idx: idx
                  }
              }
              return false;
          }
      
          private persistState = async(queue: string[]) => {
              await setStateAsync(this.stateDataId, JSON.stringify(queue));
          }
      
          private loadState = async() => {
              let state = await getStateAsync(this.stateDataId);
              if(state.val) {
                  log("State data is not null; parsing");
                  return JSON.parse(state.val);
              }
              log(`State data is null, state is: ${JSON.stringify(state)}`);
              return [];
          }
      }
      

      Wenn ich den Service nun in unterschiedlichen Modulen benutze, dann kommt es vor, dass einzelne Nachrichten nicht in die Queue gelangen. Das scheint daran zu liegen, dass der State nicht Threadsicher ist.

      Gibt es hier eine Möglichkeit, um das Problem zu umgehen? Oder Nutze ich die States vielleicht einfach falsch? Welche Alternativen gibt es?

      Vielen Dank im Voraus!

      AsgothianA Offline
      AsgothianA Offline
      Asgothian
      Developer
      schrieb am zuletzt editiert von
      #2

      @htz sagte in States und Threadsicherheit:

      Hallo liebe ioBroker Community,
      ich nutze ioBroker und den JS-Controller schon seit einer ganzen Weile.

      Im Moment arbeite ich an einem globalen Service, der Nachrichten aus verschiedenen Modulen sammeln kann und diese bei Bedarf in eingehender Reihenfolge ausgibt.

      class NotificationService {
          private readonly stateDataId = 'javascript.0.NotificationServiceData';
      
          constructor() {
              if(!existsState(this.stateDataId)) {
                  createState(this.stateDataId, JSON.stringify([]));
              }
          }
      
          public queueMessage = async(message: string) => {
              let queue = await this.loadState();
              log(`Got message to queue: "${message}"`);
              if(!this.hasMessage(message, queue)) {
                 queue.push(message);
              } else {
                  log(`NOT QUEUED: "${message}"`);
              }
              await this.persistState(queue);
          }
      
          public removeMessage = async(message: string) => {
              let queue = await this.loadState();
              let exists = this.hasMessage(message, queue);
              if(exists) {
                  queue.splice(exists.idx, 1);
              }
              await this.persistState(queue);
          }
      
          public isEmpty = async() => {
              let queue = await this.loadState();
              return this.isEmptyQueue(queue);
          }
      
          private isEmptyQueue = (queue: string[]) => {
              return queue.length === 0;
          }
      
          public sendNext = async() => {
              let queue = await this.loadState();
              if(this.isEmptyQueue(queue)) {
                  return;
              }
              log(`Queue length is ${queue.length}`);
              let message = queue.splice(0, 1)[0];
              log(`Message is: ${message}`);
              this.sendMessage(message);
              await this.persistState(queue);
          }
      
          private sendMessage = (message: string) => {
              // TODO
          }
      
          private hasMessage = (message: string, queue: string[]) => {
              log(`Queue is ${JSON.stringify(queue)}`);
              const idx = queue.indexOf(message);
              if(idx !== -1) {
                  return {
                      idx: idx
                  }
              }
              return false;
          }
      
          private persistState = async(queue: string[]) => {
              await setStateAsync(this.stateDataId, JSON.stringify(queue));
          }
      
          private loadState = async() => {
              let state = await getStateAsync(this.stateDataId);
              if(state.val) {
                  log("State data is not null; parsing");
                  return JSON.parse(state.val);
              }
              log(`State data is null, state is: ${JSON.stringify(state)}`);
              return [];
          }
      }
      

      Wenn ich den Service nun in unterschiedlichen Modulen benutze, dann kommt es vor, dass einzelne Nachrichten nicht in die Queue gelangen. Das scheint daran zu liegen, dass der State nicht Threadsicher ist.

      Gibt es hier eine Möglichkeit, um das Problem zu umgehen? Oder Nutze ich die States vielleicht einfach falsch? Welche Alternativen gibt es?

      Vielen Dank im Voraus!

      Dein problem ist das es keinerlei Interlocking zwischen sendNext und persistState gibt.

      Als Vorschlag würde ich

      • die Warteschlange als Skript globale Variable im Speicher halten, und nur beim Start des Skriptes aus dem Datenpunkt lesen
      • die Warteschlange bei jeder Anpassung durch persistState() sichern.

      Solange das Skript läuft bleibt die Schlange im Speicher erhalten und die Zugriffe sind sauber.

      A.

      ioBroker auf RPi4 - Hardware soweit wie möglich via Zigbee.
      "Shit don't work" ist keine Fehlermeldung, sondern ein Fluch.

      1 Antwort Letzte Antwort
      0
      • H Offline
        H Offline
        htz
        schrieb am zuletzt editiert von htz
        #3

        Erstmal besten Dank für deine Rückmeldung.

        @asgothian said in States und Threadsicherheit:

        die Warteschlange bei jeder Anpassung durch persistState() sichern.

        Das habe ich im Prinzip bereits versucht. In queueMessage wird nach jeder Änderung persistiert, in sendNext ebenfalls. Imho Tritt das Problem auf, wenn während eines Vorgangs auf dem State eine weitere State-Operation ausgeführt wird, etwa so:

        modulA: queueMessage("hallo")
        modulA->notificationService: loadState => state entspricht []
        modulB: queueMessage("hallo2")
        modulB->notificationService: loadState => alter state wird geladen, entspricht []
        modulA->notificationService: persistState => state entspricht ["hallo"]
        modulB->notificationService: persistState => state entspricht ["hallo2"]
        modulC->sendNext() => "hallo2" // vereinfacht
        

        Ich bräuchte gewissermaßen ein Statelock.

        modulA: queueMessage("hallo")
        modulA->notificationService: lockState
        modulA->notificationService: loadState => state entspricht []
        modulB: queueMessage("hallo2")
        modulB->notificationService: lockState => muss warten
        modulA->notificationService: persistState => state entspricht ["hallo"]
        modulA->notificationService: unlockState
        modulB->notificationService: lockState => lock wurde aufgehoben, jetzt geht es hier weiter
        modulB->notificationService: loadState => state wird geladen, entspricht ["hallo"]
        modulB->notificationService: persistState => state entspricht ["hallo", "hallo2"]
        modulB->notificationService: unlockState
        modulC->sendNext() => "hallo" // vereinfacht
        

        @asgothian said in States und Threadsicherheit:

        die Warteschlange als Skript globale Variable im Speicher halten, und nur beim Start des Skriptes aus dem Datenpunkt lesen

        Bitte korrigiere mich, falls ich falsch liege, aber Globale Variablen gibt es doch im JavaScript-Adapter eigentlich nicht? Soweit ich den Adapter verstanden habe, wird der Code der Globale Scripte einfach oberhalb des Codes eines Modules (also pro *.ts, *.js Datei) eingefügt. Demzufolge hätte ich dann ja kein einzelne globale Variable, sondern mehrere. Die würden dann alle unterschiedliche Daten enthalten, oder gibt es hier noch ein Feature das ich nicht kenne?

        1 Antwort Letzte Antwort
        0
        Antworten
        • In einem neuen Thema antworten
        Anmelden zum Antworten
        • Älteste zuerst
        • Neuste zuerst
        • Meiste Stimmen


        Support us

        ioBroker
        Community Adapters
        Donate

        591

        Online

        32.6k

        Benutzer

        82.2k

        Themen

        1.3m

        Beiträge
        Community
        Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
        ioBroker Community 2014-2025
        logo
        • Anmelden

        • Du hast noch kein Konto? Registrieren

        • Anmelden oder registrieren, um zu suchen
        • Erster Beitrag
          Letzter Beitrag
        0
        • Home
        • Aktuell
        • Tags
        • Ungelesen 0
        • Kategorien
        • Unreplied
        • Beliebt
        • GitHub
        • Docu
        • Hilfe