Seite 1 von 1

Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 10:20
von isnotnone
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

Re: Polling von Dat

Verfasst: Dienstag 16. Mai 2023, 11:13
von __deets__
Den socket zu teilen zwischen threads ist eine schlechte Idee. Wie es besser ginge, hängt stark vom Rest des Programms ab.

Re: Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 11:58
von isnotnone
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

Re: Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 12:06
von __blackjack__
@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.

Re: Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 12:30
von isnotnone
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 ?

Re: Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 12:41
von __blackjack__
@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.

Re: Polling von Daten

Verfasst: Dienstag 16. Mai 2023, 13:33
von __deets__
Warum dieses rumtheoretisieren? Statt konkreten Code zu zeigen?

Re: Polling von Daten

Verfasst: Mittwoch 17. Mai 2023, 12:39
von isnotnone
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()

Re: Polling von Daten

Verfasst: Mittwoch 17. Mai 2023, 14:12
von __blackjack__
@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.

Re: Polling von Daten

Verfasst: Mittwoch 17. Mai 2023, 17:03
von isnotnone
Wie meinst du das mit dem Cache?

Re: Polling von Daten

Verfasst: Mittwoch 17. Mai 2023, 17:35
von __blackjack__
Na halt den Wert zwischenspeichern statt ihn jedes mal neu vom MPD abzufragen. Falls das tatsächlich nötig/sinnvoll ist.

Re: Polling von Daten

Verfasst: Donnerstag 18. Mai 2023, 06:25
von isnotnone
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