ist hier threading sinnvoll?

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
Robbse1990
User
Beiträge: 16
Registriert: Montag 16. Oktober 2017, 10:29

Hallo Zusammen,

ich stehe mal wieder vor dem Problem, das ich eine Lösung für ein Problem gefunden habe, jedoch ich mir nicht sicher bin, ob der Ansatz sinnvoll ist. Vielleicht gibt es einen besseren weg?

Szenario:
thread 1: liest alle 15min Sensor (datum, Temperatur und Luftfeuchtigkeit) am Pi aus und schreibt Messdaten in Datei
thread 2: liest alle 60sec den Sensor aus, wenn die Werte kritisch sind, wird ein Plot erstellt und dieser per Mail versendet.
thread 3: erstellt immer am letzten Tag des Monats einen Graphen des Monates und versendet diesen per Mail

Problem:
Wenn ich thread 1 und 2 starte, misst er mir zwar alle 15min, jedoch 3-6 mal im Abstand von paar Sekunden. Wenn thread 1 alleine läuft, geht alles gut. Auch wird die Mail nicht immer bei Alarm gesendet, mal ja, mal nicht. Ich glaube ich habe hier einen gravierenden Fehler im Kopf.

Code: Alles auswählen

import threading
import time
from send_mail import *
from funktionen import *



#Definition der Feuchte:
max_feuchte = 65.0
test_intervall = 60 
schreib_intervall = 900
datei_pfad = 'Messdaten.txt'

#####################################################################################
#daten lesen und in datei schreiben 

class schreiben(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            #Sensor und Zeit auslesen
            zeit     = time.strftime("%a:%d.%m.%Y %H:%M:%S")
            var_temp, var_hum = sensor_read()
            #in datei schreiben     
            with open (datei_pfad, "a") as fh:
                fh.write('{}\t{}\t{}\n'.format(zeit, var_temp, var_hum))          
            time.sleep(schreib_intervall)

#####################################################################################
#alarm

class alarm(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:	
		#Sensor und Zeit auslesen
        	zeit     = time.strftime("%a:%d.%m.%Y %H:%M:%S")
        	var_temp, var_hum = sensor_read() 
 
        	if (var_temp < 10.0 or var_temp > 35.0 or var_hum > max_feuchte):

			#in datei schreiben     
        		with open (datei_pfad, "a") as fh:
                		fh.write('{}\t{}\t{}\n'.format(zeit, var_temp, var_hum))
	    		pfad_mail_anhang = plot_alarm(datei_pfad,5,max_feuchte)
            		send_mail_warnung(var_temp,var_hum,zeit,pfad_mail_anhang)
 			time.sleep(43200)#12h

		time.sleep(test_intervall) 
       

t1 = schreiben()
t2 = alarm()
t1.start()
t2.start()

narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

Moin,

ich würde das Problem eher mit einer (bzw. mehreren) systemd-Timer-Unit (`man systemd.timer`) lösen, die das Programm im jeweiligen Intervall startet.

Eventuell programmierst du auch gerade etwas wie Munin nach.

Zum gezeigten Code: Ist das wirklich der Code, den du laufen lässt? In Zeile 48 ist die Einrückung kaputt und eigentlich sollte der Code deswegen nicht laufen. Benutzt du Python 2 oder 3?

Die Klassennamen entsprechen nicht PEP 8. Klassennamen sollten in `CamelCase` geschrieben werden und Objektnamen darstellen. „Schreiben“ ist kein sinnvoller Name für ein Ding.

Eine `__init__`-Methode, in der nur die `__init__`-Methode der Elternklasse aufgerufen wird, kann ersatzlos gestrichen werden, weil das per default passiert.

Wann wird Zeile 51 ausgeführt? Und wie lange dauert ein Durchlauf der Endlosschleife in Zeile 37?

Für CSV-Dateien sollte das `csv`-Modul benutzt werden, anstatt das selber nachzuprogrammieren. Alternativ `numpy` oder `pandas`, wobei das overkill wäre, nur um eine Zeile zu schreiben.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Robbse1990: für mich stellt sich die Frage, warum Threads. Das sind drei unabhängige Programme.
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,



dein 'time.sleep(test_intervall)' aus der Klasse Alarm steht außerhalb der while - Schleife, ist das beabsichtigt?
Dadurch wird die Schleife eben alle 12h ausgeführt und nicht 60 Sekunden.
In deinem Beispielcode sind einige Einrückungen falsch.

Du schreibst übrigens zwei mal in deine Datei. Einmal mit der Klasse Alarm und einmal mit der Klasse Schreiben.
Robbse1990
User
Beiträge: 16
Registriert: Montag 16. Oktober 2017, 10:29

hier ist was mit dem code schiefgegangen bzw. mit dem einrücken. Ich habe das einfach mit copy and paste aus dem Programm übernommen, kann es sein das es einrückfehler gibt, die man nicht sieht? Dachte es ist egal ob tab oder leerzeichen, hauotsache konsistent innerhalb einer funktion:

Code: Alles auswählen

import threading
import time
from send_mail import *
from funktionen import *



#Definition der Feuchte:
max_feuchte = 65.0
test_intervall = 60 #in Sekunden (einmal die Stunde sind 3600sec) 
schreib_intervall = 900
datei_pfad = 'Messdaten_neu.txt'



#####################################################################################
#daten lesen und in datei schreiben 

class schreiben(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:
            #Sensor und Zeit auslesen
            zeit     = time.strftime("%a:%d.%m.%Y %H:%M:%S")
            var_temp, var_hum = sensor_read()
            #in datei schreiben     
            with open (datei_pfad, "a") as fh:
                fh.write('{}\t{}\t{}\n'.format(zeit, var_temp, var_hum))          
            time.sleep(schreib_intervall)

#####################################################################################
#alarm

class alarm(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        while True:	
		#Sensor und Zeit auslesen
        	zeit     = time.strftime("%a:%d.%m.%Y %H:%M:%S")
        	var_temp, var_hum = sensor_read() 
 
        	if (var_temp < 10.0 or var_temp > 35.0 or var_hum > max_feuchte):

			#in datei schreiben     
        		with open (datei_pfad, "a") as fh:
                		fh.write('{}\t{}\t{}\n'.format(zeit, var_temp, var_hum)
                pfad_mail_anhang = plot_alarm(datei_pfad,5,max_feuchte)
            	send_mail_warnung(var_temp,var_hum,zeit,pfad_mail_anhang)
                time.sleep(43200)#12h
            time.sleep(test_intervall) 
       

t1 = schreiben()
t2 = alarm()
t1.start()
t2.start()


@narpfel
Ich verwende python 2, die Idee ist, das alle 60 Sec (=testintervall) geschaut wird, ob die Werte im grünen Bereich liegen. Falls nciht, bekomme ich die Mail mit dem Graphen. Damit ich nicht alle 60 Sekunden eine Mail bekomme, soll das Programm in so einem Fall für 12 Stunden ruhen. Das gibt mir genung Zeit alles zu checken...

@sirius
ziel ist es später noch eine art forcemodus einzubinden. Wenn die Werte gewisse Zeit in einem "orangenen" Bereich liegen, soll der Testintervall angepasst und der lüfter dazugeschaltet werden. Geht vllt auch ohne threading, aber das erschien mir eleganter als 2 oder 3 Programme. Wenn das aber quatsch ist, lass ich das.

@sebastian
das war ein copy paste fehler, oben der richtige code. Es ist beabsichtigt, das im falle eines alarmes zusätzlich in die datei geschrieben wird.

@all
Danke! Freu mich über jede Kritik, Hilfe, Antwort
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Egal ob mit drei Programmen oder Units oder Threads: man muss denke ich sicherstellen, das konkurrierende Zugriffe auf den sensor ok sind. Also entweder testen, oder nachlesen, oder prophylaktisch durch ein gemeinsames lock verhindern. Letzteres ist natürlich in einer multi-thread Umgebung am einfachsten.
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit der neuen Beschreibung kann man nun sagen: dein Ansatz ist falsch. Eine solche Logik läuft ja auf verschiedene Programmzustände hinaus, durch die verschiedene Aktionen getriggert werden. Ein solches Zusammenspiel ergibt sich nicht aus dem parallelen starten von Programmteilen. Im Gegenteil.

Du musst präzise definieren, welche Zustände es gibt, und wie das Programm von einem in den anderen gelangt. Eine in der Informatik dafür gerne genommene Struktur ist die des endlichen Automaten. Damit kannst du ein solches Verhalten modellieren, und auch recht gut in ein Programm umwandeln.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Robbse1990: das Mischen von Tabs und Leerzeichen solltest Du nicht machen, auch wenn es vielleicht geht. Aber je nach Editor geht die Eintrückung dann doch kaputt. Nimm immer 4 Leerzeichen pro Ebene. Noch ein Paar Anmerkungen zum Code. Ein Modul das funktionen heißt, ist kein sinnvolles Modul, weil es keinen sinnvollen Namen hat. Module kapseln zusammengehörige Teile, einfach nur Funktionen bilden aber keine Einheit. Sternchenimporte sind generell zu vermeiden, gib die importierten Namen explizit an. Konstanten werden KOMPLETT_GROSS geschrieben, also MAX_FEUCHTE, etc. Für Deine Threads brauchst Du keine Klassen, weil sie keinen Zustand haben. Benutze das target-Argument von Thread. Die beiden gezeigten Threads machen das selbe, so dass es besser wäre nur eine Schleife zu haben und dann sind gar keine Threads mehr nötig. Zeile 44: da sind zwei Zahlen, die Du als Konstanten herausführen solltest; die Klammern sind unnötig.
Robbse1990
User
Beiträge: 16
Registriert: Montag 16. Oktober 2017, 10:29

ich habe jetzt 3 seperate Programme geschrieben.

1. Liest den Sensor zu bestimmten Zeiten aus
2. Schickt je am Monatsende einen Graphen des kompletten Monats per Mail raus
3. testet alle 60 sec die Werte und schickt Mail, falls was im roten Bereich ist

Habe eure Tipps umgesetzt, macht Sinn. Danke!
Antworten