Prioritäten bei Threads

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.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Donnerstag 10. Januar 2019, 19:18

Hallo,

ich bin immer noch mit meiner "Datenlogger" Software beschäftigt.
Die Software sollte S0 Zählerimpulse aufzeichnen und die aktuelle Leistung berechnen.

Nun zum Problem:
Die S0 Eingänge werden per i2c abgefragt - diese Abfrage sollte im 30ms Zyklus oder schneller geschehen.

Sporadisch (ca. alle 1-2 Stunden) wird ein Impuls nicht erkannt, weil der "Abfragezyklus" länger als 30ms gedauert hat.
Dies geschieht wenn gerade in anderen Threads eine Rechnung gemacht wird.

Hat jemand eine Idee, wie ich den Thread "Einlesen" fix im 30ms Takt hinbekomme.

Hab an multiprocessing gedacht und dann in Linux die Prozesse priorisieren, dann hab ich aber wieder das Problem mit dem
Datenaustausch zwischen "Einlesen" und "Berechnen".

Vielen Dank für eure Tipps.
Daniel
Benutzeravatar
noisefloor
User
Beiträge: 2683
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Donnerstag 10. Januar 2019, 20:07

Hallo,

Antwort: gar nicht.

1. Entscheidet der OS Kernel, wann Python Rechenzeit bekommt und 2. dann kommt noch der (mehr oder minder komplexe) Mechanismus des Thread-Switchings von (C)Python dazu.

Sehr empfehlenswertes Video dazu: https://www.youtube.com/watch?v=Obt-vMVdM8s

Welches Problem hast du denn mit dem Datenaustausch (bzw. wenn es schon Threads hier im Forum gibt gerne auch einen Link dorthin). Es gibt da ja diverse Möglichkeiten.

Gruß, noisefloor
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Donnerstag 10. Januar 2019, 20:47

Hab's noch garnicht versucht.
Hab nur ca. 10 Bool die ich austauschen muss - was ist die simpelste Lösung dafür?
Benutzeravatar
noisefloor
User
Beiträge: 2683
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Donnerstag 10. Januar 2019, 21:58

Hallo,

musst du Daten von A nach B senden oder teilen sich zwei Programmteile die Daten gemeinsam?

Gruß, noisefloor
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Donnerstag 10. Januar 2019, 22:38

Von A nach B reicht.

Lg
Daniel
__deets__
User
Beiträge: 5589
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 11. Januar 2019, 01:11

Wie noisefloor schon richtig sagt - das Python-eigene Thread Management macht Probleme. Wenn du aber multiprocessing verwendest, und in einem Prozess NUR das I2C lesen betreibst, sollte das funktionieren. Denn dann kannst du den I2C-Prozess mit eine Echtzeitprioritaet ausstatten. Wichtig ist aber, das der dann nur das absolut notwendige tut!

Ggf kannst du auch nixh die Kerneloption threaded-irq benutzen. Oder gar einen PREEMPT_RT Kernel nutzen.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Freitag 11. Januar 2019, 08:19

Hab alls Test mal ein Script gemacht das nur einliest - bleibt im Bereich 1-3ms, das wäre super.
Hab die Priorität mit "nice" gemacht, funktioniert auch super.

Nun stellt sich noch die Frage, wie die Variablen dem "langsameren" prozess zur verfügung stellen.
Es gibt ja mehrere ansätze, welcher ist zu empfehlen?
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Freitag 11. Januar 2019, 08:42

Code: Alles auswählen

import multiprocessing
import time
import os

def slow_proc():
    os.nice(10)
    while True:
        print 'worker1'
        time.sleep(0.1)

def fast_proc():
    os.nice(-10)
    while True:
        print 'worker2'
        time.sleep(0.1)

if __name__ == '__main__':
    slow = multiprocessing.Process(target=slow_proc)
    slow.start()

    fast = multiprocessing.Process(target=fast_proc)
    fast.start()
So funktioniert das ganz gut mit dem priorisieren
Benutzeravatar
noisefloor
User
Beiträge: 2683
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Freitag 11. Januar 2019, 08:57

Hallo,
Nun stellt sich noch die Frage, wie die Variablen dem "langsameren" prozess zur verfügung stellen.
Um Daten von A nach B zu transportieren bietet sich eine Queue an. Python hat dafür ein passendes Modul Namens `queue` an Bord.

Wenn die Daten viel schneller kommen als diese von einem Prozess verarbeitet werden können, können aus einer Queue auch mehrere Prozess lesen.

Gruß, noisefloor
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Montag 14. Januar 2019, 15:30

Hallo,

ich habe nun einiges versucht - trozdem ist dei "zykluszeit" manchmal über 30ms.

Kann ich diesen Code "verschnellern", dies ist die Abfrage im schnellen Teil der Software?

lg
Daniel
#i2cdata
channel1_input = not (bool(i2cdata & (2 ** 0)))
channel2_input = not (bool(i2cdata & (2 ** 1)))
channel3_input = not (bool(i2cdata & (2 ** 2)))
channel4_input = not (bool(i2cdata & (2 ** 3)))
channel5_input = not (bool(i2cdata & (2 ** 4)))
channel6_input = not (bool(i2cdata & (2 ** 5)))

#channel1
channel1_ppulse = channel1_input and not channel1_ppulse_hm
channel1_ppulse_hm = channel1_input
if channel1_ppulse is True:
if channel1_active is True:
channel1.impuls()

#channel2
channel2_ppulse = channel2_input and not channel2_ppulse_hm
channel2_ppulse_hm = channel2_input
if channel2_ppulse is True:
if channel2_active is True:
channel2.impuls()

#channel3
channel3_ppulse = channel3_input and not channel3_ppulse_hm
channel3_ppulse_hm = channel3_input
if channel3_ppulse is True:
if channel3_active is True:
channel3.impuls()

#channel4
channel4_ppulse = channel4_input and not channel4_ppulse_hm
channel4_ppulse_hm = channel4_input
if channel4_ppulse is True:
if channel4_active is True:
channel4.impuls()

#channel5
channel5_ppulse = channel5_input and not channel5_ppulse_hm
channel5_ppulse_hm = channel5_input
if channel5_ppulse is True:
if channel5_active is True:
channel5.impuls()

#channel6
channel6_ppulse = channel6_input and not channel6_ppulse_hm
channel6_ppulse_hm = channel6_input
if channel6_ppulse is True:
if channel6_active is True:
channel6.impuls()
time.sleep(0.0005)
Benutzeravatar
sparrow
User
Beiträge: 1165
Registriert: Freitag 17. April 2009, 10:28

Montag 14. Januar 2019, 15:40

Das ist sequentiell ablaufender Code. Die Crux wird irgendwo in channel.impuls() liegen un die Funktion kennen wir nicht.

Du benutzt hier 6x fast identischen Code. Das schreit regelrecht danach, dass das in Schleifen gelöst und die Daten in Listen gespeichert werden. Als Faustregel gilt: Wenn man anfängt Variablen zu nummerieren, muss man sich Gedanken machen, das zu bündeln.
Dann ist der ganze Abschnitt so um die 10 Zeilen Code, wenn überhaupt. Und du würdest die Fehlerpotentiale erheblich reduzieren.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Montag 14. Januar 2019, 15:53

Im Impuls wird nicht all zu viel gemacht:
def impuls():
# print 'channel1 - PULSE'
last_pulstime.value = act_pulstime.value
act_pulstime.value = time.time()
channel_logger_count.info(str(setpoint))
Ändert dies etwas an der Geschwindigkeit wenn es in einer Schleife gemacht wird?
Benutzeravatar
__blackjack__
User
Beiträge: 3350
Registriert: Samstag 2. Juni 2018, 10:21

Montag 14. Januar 2019, 16:06

@Muntliger: Jetzt verschiebt sich das zu was auch immer in ``channel_logger_count.info(str(setpoint))`` gemacht wird. Logger können ja beliebig komplizierte Dinge tun.

Ja, an der Geschwindigkeit ändert sich etwas, das ist aber nicht der Punkt. Eine Schleife ist kürzer, leichter lesbar, besser wartbar und damit weniger fehleranfällig. Wenn Du manuelles „loop unrolling“ machen willst, dann ist Python an der Stelle die falsche Programmiersprache.

Was Du auch sein lassen solltest ist das Vergleichen mit literalen Wahrheitswerten. Wobei ``is`` auch erst in aktuellen Python 3-Versionen überhaupt erst erlaubt ist. Und die beiden verschachtelten ``if``-Bedingungen hätte ich auch in *eine* gesteckt.
“I am Dyslexic of Borg, Your Ass will be Laminated” -- unknown
__deets__
User
Beiträge: 5589
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 14. Januar 2019, 16:50

Die Faustregel ist: wenn du Echtzeit machen willst, mach keine IO. Du kommst um deine I2C-Calls nicht herum, aber alles andere, was nicht noetig ist, solltest du vermeiden.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Montag 14. Januar 2019, 17:08

was meinst du mit IO?
Antworten