Polling von Daten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

Hallo Python Gemeinde,
also ich habe eine Program erstellt was quasi in einem Thread periodisch auf einen Netzwerk Socket zugreift und dort Daten abfragt.
Jetzt habe ich einmal einen Thread der immer nach so ca. 5 Sekunden Daten vom Socket abfragt.
Gleichzeitig habe ich im Programm an anderen Stellen noch Zugriff auf den Socket ohne Thread.
Manchmal kommt es vor das der Thread scheibar stolpert und keine neuen Daten bekommt.

Nun mir geht es darum wie kann man das besser lösen:

Sollte ich alle Zugriffe auf den Socket via Thread machen ?
Wie ist da eine gute Vorgehensweise?

Danke schon mal
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Den socket zu teilen zwischen threads ist eine schlechte Idee. Wie es besser ginge, hängt stark vom Rest des Programms ab.
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

Also der Thread ruft periodisch die Playlist inklusive aller Song Informationen ab , der andere fragt ab welcher Song gerade gespielt wird ..... vielleicht hilft das dir ein wenig
Benutzeravatar
__blackjack__
User
Beiträge: 13143
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@isnotnone: Das bestätigt im Grunde nur noch mal dass das eine schlechte Idee ist, denn wenn die nicht irgendwie sauber synchronisiert sind, dann können die sich gegenseitig in die Quere kommen.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

ok danke
also muß ich jede Anfrage in einem Thread machen
Und die Threads müßten untereinander synchronisiert werden
Kannst du mir ein paar Stichworte empfehlen oder ein Tutorial ?
Benutzeravatar
__blackjack__
User
Beiträge: 13143
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@isnotnone: „[J]ede Anfrage in einem Thread“ ist mehrdeutig. Falls damit gemeint ist jede Anfrage in einem *eigenen* Thread der nur eine Anfrage macht — nein das muss man nicht. Jede Anfrage in *einem* Thread, der nur für die Anfragen zuständig ist, kann man auch machen, das wäre auch nicht unbedingt eine schlechte Idee.

Stichwort ist „nebenläufige Programmierung“ oder „concurrent programming“ und das ist normalerweise Stoff für ein ganzes Semester. Und in der Praxis hängt das auch alles davon ab was man ganz konkret macht und welche Rahmenwerke beteiligt sind. GUI-Rahmenwerke haben beispielsweise fast alle ihre eigenen idiomatischen Lösungen Threads zu integrieren. Falls man kein GUI-Rahmenwerk verwendet, bietet es sich oft an eine eigene Ereignisschleife in seinem Hauptprogramm zu basteln.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Warum dieses rumtheoretisieren? Statt konkreten Code zu zeigen?
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

Ich versuche mal die Klassen zu posten die daran beteiligt sind ...
Traue mich eigentlich immer nicht, aber ich mach das mal trotzdem jetzt, bitte mich nicht in der Luft zerreißen :roll: :lol:

Der Proxy der auf den MPD zugreift

Code: Alles auswählen

class MPDProxy:
    def __init__(self, host="mpdhost", port=6600):
        self.client = mpd.MPDClient()
        self.host = host
        self.port = port
        # number of (reconnects)
        self.reconnects = 0
        self.isConnected = False
        self.connect(host, port)

    # because we do not know what function will be called, we use __getattr__
    # eg. mpd.idle() ->  __getattr__ self: <mpd_proxy.MPDProxy object at 0x7f5fa7265b70>, name: idle
    def __getattr__(self, name):
        #if self.isConnected: #debug not shure if neccesary
        return self._call_with_reconnect(getattr(self.client, name))

    def connect(self, host, port):
        what = "connecting" if self.reconnects == 0 else "reconnecting"
        logging.info(f"{self.reconnects}:{what} ...")
        self.reconnects bananapi+=1
        self.client.connect(host, port)

        #if self.client.mpd_version:
        if self.client:
            self.isConnected = True
            logging.info(f"MPDPROXY connected to {host}:{port}")
        else:
            self.isConnected = False
            logging.info(f"MPDPROXY not connected!")

    def _call_with_reconnect(self, func):
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except mpd.ConnectionError as e:
                self.isConnected = False
                self.connect(self.host, self.port, )
                logging.error(f"{e}")
                return func(*args, **kwargs)
            except OSError as e:
                self.isConnected = False
                logging.error(f"{e}")
                self.client.disconnect()
                self.client = mpd.MPDClient()
                pass
            except Exception as e:
                logging.error(f"{e}")
                pass
        return wrapper
Der Fetcher

Code: Alles auswählen

import time, os
from threading import Thread, Event
# logging
import logging
from util import *

from mpd_proxy import MPDProxy
import tkinter as tk

class Fetcher(Thread):
    def __init__(self, playerApp = None):
        Thread.__init__(self)
        self.stopped = Event()
        self.playerApp = playerApp
        # to get/set config user self.playerApp handle
        self.client = self.playerApp.getClient()
        self.currentsong = None
        self.config = playerApp.appconfig
        self.fetcherIntervall = self.config.get("fetcherIntervall")

    def run(self):
        # https://stackoverflow.com/questions/12435211/threading-timer-repeat-function-every-n-seconds#12435256
        name = self.__class__.__name__
        while not self.stopped.wait(int(self.fetcherIntervall)):
            logging.debug(f"THREAD is running {self.client}/{self.fetcherIntervall}....")

            self.client = self.playerApp.getClient()
            if not self.client: logging.error("No Client (None) ")
            if self.client:
                logging.debug("Fetching Data ...")
                self.currentsong = self.client.currentsong()
                logging.debug(self.currentsong)

    def stop(self):
        name = self.__class__.__name__
        logging.info(f"{name}: Thread stops ...")
        self.stopped.set()
Daten holen indem auf die Klassenvariable zugegriffen wird
song = fetcher.currentsong

dann gibt es aber auch noch Zugriffe auf den MPDProxy ohne Thread
self.client = MPDProxy(server, port)
client.status().get("state")

Zusammenfassend:
Es gibt einen Thread der immerfort Song Daten abfragt, der Zugriff erfolgt über fetcher.currentsong
Es gibt Anfragen an den MPDProxy ohne Thread via client.getSth()
Benutzeravatar
__blackjack__
User
Beiträge: 13143
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@isnotnone: Was ist denn der Sinn von diesem Thread? Warum nicht einfach den aktuellen Song vom MPD abfragen wenn er gebraucht wird? Falls das ”zu oft” der Fall ist, kann man ja einfach einen Cache basteln der nur nach x Sekunden tatsächlich neu abfragt.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

Wie meinst du das mit dem Cache?
Benutzeravatar
__blackjack__
User
Beiträge: 13143
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Na halt den Wert zwischenspeichern statt ihn jedes mal neu vom MPD abzufragen. Falls das tatsächlich nötig/sinnvoll ist.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
isnotnone
User
Beiträge: 6
Registriert: Dienstag 16. Mai 2023, 09:24

Hab jetzt den Thread auf Eis gelegt ;-).
Nutze so wie es __blackjack__ vorgeschlagen den direkten Aufruf ohne Thread

Code: Alles auswählen

self.after(seconds, self.update)
self.update benutzt zusätzlich noch einen Cache
Damit tritt der Fehler nicht mehr auf
Antworten