Nicht blockierendes sleep innerhalb eines Moduls?

Begonnen von mumpitzstuff, 07 Dezember 2017, 12:47:56

Vorheriges Thema - Nächstes Thema

mumpitzstuff

Gibts die Möglichkeit innerhalb eines Moduls ein nicht blockierendes Sleep zu verwenden? Wenn ja wie? Einfach nur sleep(1) im Code? Mir ist das nicht so ganz klar, weil FHEM ein nicht blockierendes Sleep integriert hat, das von Perl aber gleich heißt (das ist blockierend). Was wird jetzt verwendet wenn man einfach sleep(1) ins Modul rein schreibt?
Welche Möglichkeiten gibt es sonst noch, ohne über zusätzliche Timer und Funktionen den Code zerpflücken zu müssen (sollte immer non blocking sein)?

Hintergrund:
Ich muss ziemlich viele Requests an einen Server stellen, überlaste diesen jedoch, wenn ich alle auf einmal raus ballere. Ich will das deshalb etwas zeitlich entzerren. Vollständig serialisieren will ich das nicht, das würde zu viel Zeit kosten.

justme1968

nein. mit sleep geht das nicht.

falls es http ist: die httputils im nonblocking mode funktionieren gut. wenn es dir rein seriell zu langsam geht kannst du ja mehr als eine anfrage auf einmal absetzen und dann immer wenn eine zurück kommt eine weitere hinterher schieben.

wenn es ein socket ist kannst du non blocking io verwenden und es in die select loop einhängen und reagieren wenn du wieder schreiben kannst.

bist du sicher das rein seriell zu langsam ist? wenn's er server schon in die knie geht ist der limitierende faktor vermutlich nicht die netzwerk round trip zeit.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

DS_Starter

Eine Idee/Anregung hätte ich noch unter Benutzung von BlockingCall (Blocking.pm).
Du verzweigst damit in einen nebenlaufenden Prozess und arbeitest dort alles ab. Dort kannst du auch mit einem blockierenden perl sleep arbeiten.
Das tut nichts.
Nachteil ist, dass du Auswertungen, Variablen, Hash-Werte usw. erst wieder an den Hauptprozess übergeben kannst wenn der BlockingCall zu Ende ist und die FinishFn aufgerufen wird.
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

rudolfkoenig

ZitatNachteil ist, dass du Auswertungen, Variablen, Hash-Werte usw. erst wieder an den Hauptprozess übergeben kannst wenn der BlockingCall zu Ende ist und die FinishFn aufgerufen wird.
Das geht auch vorher mit BlockingInformParent, als Beispiel siehe update_Log2Event in 98_update.pm

betateilchen

Zitat von: mumpitzstuff am 07 Dezember 2017, 12:47:56
Mir ist das nicht so ganz klar, weil FHEM ein nicht blockierendes Sleep integriert hat, das von Perl aber gleich heißt

Wenn Du in einem Modul sleep() verwendest, wird immer die perl Funktion verwendet.

Das sleep in FHEM ist ein FHEM-Befehl und deshalb nicht unter diesem Namen als Funktion verfügbar.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

DS_Starter

ZitatDas geht auch vorher mit BlockingInformParent, als Beispiel siehe update_Log2Event in 98_update.pm

Ah... interessant. Danke für die Info Rudi. Kann ich gut gebrauchen.

Grüße
Heiko
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

Wzut

Zitat von: DS_Starter am 07 Dezember 2017, 13:21:54
Danke für die Info Rudi. Kann ich gut gebrauchen.
@DS_Starter , mach mal im FHEM Unterordner ein grep InformParent *.pm
Ausser Rudi , HCS und Tobias scheint das bisher auch kein anderer Modulautor auf dem Radar zu haben :)
( Ich selbst will da noch 2017 mit zwei Modulen dazu .... )
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

DS_Starter

ZitatAusser Rudi , HCS und Tobias scheint das bisher auch kein anderer Modulautor auf dem Radar zu haben :)
Stimmt, bin zwar immer mal wieder darüber gestolpert, aber richtig wahrgenommen/verarbeitet habe ich es bisher nicht.
Bis jetzt war ich auch ganz gut ohne diese Möglichkeit ausgekommen, aber letztens bin ich bei einer Aufgabenstellung ohne dieses Feature nicht vorwärts gekommen und habe andere Verrenkungen zur Lösung des Problems gemacht.
Mir ist das einfach nicht eingefallen .... werde mir die Sache nochmal unter diesem Aspekt neu vornehmen ....
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

mumpitzstuff

#8
Das Problem ist leider nicht so trivial wie es aussieht.

Es handelt sich hierbei um ein WLAN Radio, bei dem man diverse Informationen immer wieder abholen muss (polling). Den Ansatz BlockingCall zu verwenden hatte ich schon mal, aber dann passiert folgendes:

Request
Warten
Antwort
Request
Warten
Antwort

Insgesamt dauert das zu lang bei bisher rund 30 Anfragen an das Radio. Ob das funktionieren würde aus einem BlockingCall heraus asynchron mit CallBacks zu arbeiten, weiß ich nicht, das Thema war mir zu heiß. Wenn mir jemand sagt, dass das geht, dann versuche ich aber gern auch das noch mal.
Ich habe deshalb die Non Blocking Funktionen der httputils verwendet und im ersten Schritt einfach in der Update Funktion 30 Requests ans Radio rausgeballert. Nach etwa 20 Antworten bekomme ich für die restlichen 10 entweder timeout oder leere Daten.
Um das zu umgehen kann ich jetzt entweder zwischen den Requests etwas warten und damit dem Radio Zeit geben bereits geschickte Requests zu beantworten. Super wäre ein sleep in irgend einer Art gewesen, weil sich das nahtlos in den bestehenden Code integrieren lassen würde. Da das nicht geht, kann ich nur durch die Callbacks getriggert das Senden der Pakete starten. Und jetzt kommt das ABER: Die Reihenfolge der Antworten ist unbekannt. Es passiert also folgendes:

Request 1
Request 2
Request 3
Warten
Antwort 3
Antwort 1
Antwort 2

Ich kann damit also nicht sagen: wenn du die Antwort von Request 3 empfangen hast, dann starte die nächsten 3 Requests. Schlussendlich bedeutet dass, ich muss mir den Status aller 3 Requests merken und wenn alle 3 angekommen sind, dann darf ich erst die nächsten 3 Requests schicken. Und da wird die Handhabung dann irgendwie extrem kacke und der Ansatz fliegt deshalb erst mal raus.

Die zweite Idee die ich habe ist:

timer1 -> Update1()
timer2 = timer1 + 2 -> Update2()
timer3 = timer2 + 2 -> Update3()

Update1()
Update2()
Update3()

Dann sind die Update Pakete zeitlich voneinander etwas entkoppelt und das Radio sollte genug Zeit haben die Requests zu beantworten. Vom Code her immer noch bescheiden, aber wahrscheinlich robuster als Version 1.

CoolTux

Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

mumpitzstuff

Idee gefällt mir gut.

Update (timer alle x Sekunden (mindestens 20)) -> 30 Requests -> Queue

Queue (timer jede Sekunde) -> 5 Requests los schicken und per Callback verarbeiten

Ich denke das werde ich so angehen. Danke!

CoolTux

Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net