Globale Array lesen schreiben

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
Radius
User
Beiträge: 4
Registriert: Montag 11. Februar 2019, 22:07

Hallo zusammen.

Bin gerade dabei ein C-Control Programm umzuschreiben auf Python 3
Das Python Programm besteht aus meheren threads.
Ein thread ist nur für den empfang von Daten der seriellen Schnittelle zuständig,
diese verarbeitet er dann zu 32 Temperaturwerten und schreibt sie dann per
numpy.copy() in die globale Array.
Alle anderen threads (5 Stück) greifen nur lesend auf die Globale Array zu.
So jetzt zu meiner eigentlichen Frage, kann es zu Korrupten Array Werten kommen
wenn zur gleichen Zeit ein Wert aus der Array gelesen wird und die Array gerade
per numpy.copy() beschrieben wird. Ich meine so was das aus dem eigentlichen Wert "23"
der Wert "3" wird weil nur die hälfte geschrieben oder gelesen werden kann.

Habe schon den großen Bruder Google danach befragt, bin aber nicht richtig fündig geworden
oder habe es einfach nicht versenden.
Das Python Programm sollte mal das Ersetze bzw. erweitern. http://www.radis-keller.de/heizung/index_02.php

Danke schon im voraus
Radius
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Puh. Ganz schön schwer zu sagen. durch die C-Implementierung von numpy ist das nicht so einfach zu beantworten. Um sicher zu gehen könntest du

- immer eine Kopie im schreib-Thread machen.
- die erweitern.
- das neue Array an den globalen Namen binden. Diese Operation ist durch das GIL geschützt.


Alternativ natürlich locking.

Insgesamt würde ich deinen Ansatz aber in frage stellen. Wozu so viele Threads? Mit zb asyncio sollte sowohl das lesen der seriellen Schnittstelle als auch sämtliche Weiterverarbeitung mit nur einem Thread gelingen.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Radius: was sollen denn die 5 Threads tun? Wenn es um große Rechnungen geht, ist Python mit Threads dafür nicht geeignet, es sei denn, es sind einzelne numpy-Operationen, die den GIL freigeben. Einfacher zu verstehen und leichter fehlerfrei zu bekommen, wäre ein serieller Ansatz, die 32 Werte lesen und danach 5 Verarbeitungsfunktionen aufzurufen.

Zur Frage: Garantien zu geben, ist schwierig, vor allem, wenn wirklich mehrere Kerne gleichzeitig auf den selben Arbeitsspeicher zugreifen. Was passiert, wenn erst 7 der 32 Werte kopiert wurden? Gibt es dann noch ein konsistentes Ergebnis?

1. keine Threads benutzen, wenn nicht wirklich nötig. Dazu müßten wir wissen, was Du wirklich tust.
2. falls doch, Kommunikation nur über entsprechende Methoden. Keine globalen Variablen. Hier also, eine Queue für jeden Thread, die vom Lese-Thread mit den neuen Temperaturwerten gefüttert wird.
Radius
User
Beiträge: 4
Registriert: Montag 11. Februar 2019, 22:07

@Sirius
@deets
Danke für die schnellen antworten, werde heute Abend versuchen zu beschreiben warum
ich das Programm so aufgebaut habe.
Es hat sich leider heute Nacht jemand oder mehrere für mein Hausnetzwerk sehr interessiert dass
ich erst alles von Netz nehmen musste. Bin also sozusagen offline.

Gruß
Radius
Radius
User
Beiträge: 4
Registriert: Montag 11. Februar 2019, 22:07

@Sirius

Die 5 Threads kommen noch von dem „C-Control II“ Programm, da war es kein Problem
wenn mehrere Threads auf die selben Globalen Variablen zugreifen. Die Globale Variable wurde
mit Capture vor dem beschreiben für alle anderen Threads gesperrt das erfolgte Atomar,
nach dem Schreiben wurde Sie wieder freigegeben und alles lief ganz normal weiter.

Das Programm ist eigentlich eine kleine einfache SPS für den Heizungsbau bzw. Kälte und Wärmetechnik.

Thread_1: Mischerkreissteuerung (1-5 Mischerkreise)
Dieser Thread besteht aus 55 Lokalen Variablen und ein paar Globalen (Temperaturen) und 5 Schleifenduchläufen. Das Ergebnis jeden Durchlaufs wird in eine Funkion übergeben die Schaltbefehle ausführt ( zb. Pumpen ein oder ausschalten Mischermotoren um 3 Sekunden
öffnen oder schließen. Jeder Schleifendurchlauf kommt nach ca. 30 Sekunden wieder dran, brauche diese lange Totzeit wegen der Trägheit von Mischerkreisen.

Thread_2: Drehzahl geregelte Verbraucher (1-12 Pumpen)
Dieser Thread besteht aus 96 Lokalen Variablen und ein paar Globalen (Temperaturen) und 12 Schleifenduchläufen. Jeder Schleifendurchlauf kommt nach ca. 12 Sekunden wieder dran.Das Ergebnis jeden Durchlaufs wird in eine Funkion übergeben die Schaltbefehle ausführt ( zb. Pumpen, Lüfter ein oder ausschalten bzw. dessen Drehzahl festlegt.

Der Größte Thread hat 378 lokale Variablen entsprechend viele Globale und 18 Schleifenduchläufe. Sowie eine Schleifendurchlauf von 18 Sekunden.

So hat halt jeder Thread speziellen Regelstechnischen Aufgaben und Totzeiten. Ein Verbraucher kann auf von mehreren Threads angesprochen werden, dabei besitzt jeder Thread unterschiedliche Prioitäten.

Hoffe ich konnte es einigermaßen verständlich erklären „schreiben ist halt nicht gerade meine stärke“ :-)

Gruß Radius
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich weiß nicht, ob es für solche Anwendungsfälle schon spezielle Frameworks gibt.

55, 96 oder 378 sind zu viele lokale Variablen, als dass man sie nicht irgendwie strukturieren/modularisieren möchte. Globale Variablen sollte es gar nicht geben.
Ich weiß jetzt nicht, wie komplex Deine Berechnungen sind, aber eine Auftrennung in einzelne Outputs wäre sinnvoll.

Hardware mag es meist nicht, wenn mehrere Prozesse gleichzeitig etwas ansteuern wollen, von daher sind Threads zu vermeiden.
Um also die Kontrolle zu haben, braucht man eine Dauerschleife, mit drei Schritten:
1. Wenn Input-Daten vorliegen, diese Lesen und als "Zustand der Welt" in einer passenden Datenstruktur speichern. Zeit kann auch ein Input sein.
2. Aus den Inputs die nötigen Aktionen ableiten. Wichtig ist hier, dass die Einzelaktionen klar bestimmten Inputs zugeordnet werden, um die Übersicht zu behalten (Modularität, keine Monsterfunktion).
3. Aktionen ausführen.

Schritt zwei sollte möglichst gekapselt sein, so dass man diesen leicht mit Dummy-Inputs testen kann, daher auch die Aktionen nicht direkt ausführen, sondern getrennt in Schritt 3.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ist jetzt ein bisschen schwer nachzuvollziehen, was genau da passiert. Aber ich sehe trotzdem erstmal keinen Zusammenhang zwischen deinen X Steueraufgaben, und der Notwendigkeit dafuer einen Thread zu starten. Und sich damit all die Probleme einzufangen. Ich vermute mal eher, dass das durch den Wunsch/die Notwendigkeit, mit Timern zu arbeiten kommt. Und genau da kann asyncio zB helfen.

Code: Alles auswählen

import asyncio

async def pumpensteuerung():
    while True:
        print("pump was")
        await asyncio.sleep(1)

async def solarsteuerung():
    while True:
        print("solarsteuer was")
        await asyncio.sleep(.3)

def main():
    loop = asyncio.get_event_loop()
    loop.create_task(pumpensteuerung())
    loop.create_task(solarsteuerung())
    loop.run_forever()

if __name__ == '__main__':
    main()
Und wenn du so vorgehst, dann musst du keine Threads haben, und hast in dem Moment auch keine Synchronisation und Probleme damit.
Radius
User
Beiträge: 4
Registriert: Montag 11. Februar 2019, 22:07

@deets
Danke für die schnellen antworten,
Ihr habt mich überzeugt, werde versuchen das Programm so umschreiben das es ohne Threads auskommt.
Manchmal verrennt man sich halt in alte Strukturen und kommt halt fast ohne Hilfe nicht mehr raus.

Gruß Radius
Antworten