Multiprocessing bei getrennten Prozessen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Hallo Forum,
mein Name ist Micki, 66 Jahre und neu im Thema Python und Raspi. Ich moechte einen Keyboard-Controller bauen, der aus 2 Teilen besteht:

- Teil 1 ('spielen') stellt den Durchfluss von MIDI-Daten dar. Diese Daten werden durch den Controller-Teil
manipuliert.
- Teil 2 ('tasten' ) ist der Controller-Part. Das sind lediglich Taster an den GPIOs, mit denen die ChannelNo, die
Tonhoehe oder die Anschlagstaerke gesteuert werden.

Beide Teile laufen zZt. in getrennten Scripts und auch nur einzeln.

Ich moechte aber diese beiden Teile per Multiprocessing in einem Skript zusammenfassen.

Zur Erlaeuterung:
Die Multiprocessing-Scripte, die ich hier gesehen habe, befassen sich (soweit ich das verstehe) mit dem Aufruf mehrerer Threads oder Prozesse, die dann aber alle das Gleiche machen und am Ende Daten verdichten.

Ich moechte nun meine beiden Prozesse gleichzeitig laufen lassen, wobei 'spielen' in einer ewigen Schleife laeuft und Midi-Signale empfaengt und dann ggf bearbeitet an den Ausgang weiterleitet.
Der Part 'tasten' laeuft ebenfalls in einer Schleife und wartet darauf, das Knoepfe gedrueckt werden, worauf sich dann Parameter aendern, die sich auf das musikalische Ergebnis auswirken.

Es geht also nur um den Start der beiden Prozesse. Bezueglich der Parameter sind keine Kollisionen zu befuerchten, da jeder Parameter nur eine Quelle und eine Senke hat und da es nur eine Datenflussrichtung gibt, gibt es auch keine Datenschutz-Probleme.

Es waere prima, wenn mir jemand helfen koennte. In diesen Corona-Zeiten ist es toll, wenn man sich kreativ beschaefftigen kann.

Bleibt gesund
Micki
Sirius3
User
Beiträge: 18227
Registriert: Sonntag 21. Oktober 2012, 17:20

Mit Code sind Erklärungen meist viel einfacher. Eigene Prozesse zu erstellen, wäre eher falsch, dann schon Threads. Aber, GPIOs haben callbacks, die aufgerufen werden, wenn sich was am Eingang ändert. Damit sollte sich Dein Problem ohne Threads lösen lassen.
Zuletzt geändert von Sirius3 am Freitag 20. März 2020, 10:06, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ohne Code zu sehen kann man da nicht so viel zu sagen. Wenn die beiden Teile dermassen unabhaengig sind, wie du es gerade beschreibst, dann sehe ich auch nicht, warum man da multiprocessing (im Sinne des Python-Moduls) nutzen sollte. Dann kannst du einfach beide Teile als jeweils einen Prozess starten, und gut ist. Oder gibt es eine Einwirkung von spielen auf tasten?
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Ja, das hatte ich ja beschrieben. Der Part ‚tasten‘ liefert Parameter, die ‚spielen‘ beeinflussen. Aber kollosionsfrei.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na so ganz klar ist das nicht gewesen. Seid's drum. Aber wie Sirius3 schon schrieb, GPIOs kennen Event-Callbacks, und das ist der Weg nach vorn. Denn die werden schon in einem eigenen Thread abgearbeitet. Die kannst du zB via Queue dann in der MIDI-Schleife eine Wirkung entfalten lassen.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Bitte, bitte: es geht nicht um die GPIOs.

Es geht nur um den Start von 2 Prozessen.
Der eine heißt ‚spielen‘, der andere heißt ‚tasten‘. Wie starte ich 2 Prozesse via multiprocessing?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ok, wenn du es besser weisst - hier: https://docs.python.org/3.7/library/mul ... ssing.html Da steht alles drin, vom Start mehrere Prozesse, und Kommunikation zwischen diesen, bis hin zu geteiltem Zustand.

Was daran KONKRET ist dir unklar? Hast du damit experimentiert? Was geht nicht?
Benutzeravatar
DeaD_EyE
User
Beiträge: 1206
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ich würde es nicht mit zwei getrennten Prozessen machen. Beide Aufgaben sind nicht CPU-Intensiv, können also in zwei Threads ausgeführt werden, falls das überhaupt erforderlich ist.
Die GPIOs können callbacks auslösen und die callbacks können dann z.B. Objekte verändern, die dann das Verhalten der Midi-Transformation verändern.
Man bestimmen ob man die steigende Flanke, fallende Flanke oder beide Flanken erfasst.

Ganz grob könnte das so aussehen:

Code: Alles auswählen

class MIDI:
    def __init__(self): ...

    def run(self):
        """
        Hier ist die Schleife, die dein Programm abarbeitet
        Hier kann z.B. auf Attribute der Instanz zugegriffen werden,
        die dann das Verhalten der Transformation beeinflussen.
        """
        while True:
            ...

    def callback(self, channel):
        """
        Diese Funktion wird später durch die fallende oder steigende Flanke am GPIO-Eingang aufgerufen.
        Der Kanal wird als Argument mit übergeben, d.h. man kann auch mehr als ein Signal verarbeiten.
        """
        if GPIO.input(channel):
            self.var_a = True
        else:
            self.var_a = False


gpio_pin = 22
midi = MIDI()

GPIO.setmode(GPIO.BCM)
GPIO.setup(gpio_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# GPIO.BOTH == steigende und fallende Flanke
GPIO.add_event_detect(gpio_pin, GPIO.BOTH, callback=midi.callback, bouncetime=200)
Multiprocessing würde den Code noch mehr aufblasen.
Da werden gleich sicherlich wieder irgendwelche Leute laut los schreien wie Kacke RPi.GPIO ist.
Es gibt auch andere. Das Modul gpiozero finde ich ganz in Ordnung. Was du nimmst, bleibt dir überlassen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Whow, danke,
das sieht gut aus. Aber trifft nicht ganz mein Problem.
Der Punkt ist, dass der Prozess "spielen" zeitkritisch ist. Deshalb soll er von dezidierten Kernen gefahren werden.
Wichtig ist mir auch, dass die queue sauber ein- und ausgelesen wird.

Die Erfahrung, ob multiprocessing das Richtige ist, würde ich gerne selber machen. Ich weiß nur nicht, wie es geht.

Ich denke an sowas:
(kommt gleich)
Bild

import multiprocessing


def raufrechnen(object):
for i in range(0, 100):
z=z+i
print('z= ', )
def runterrechnen(object):
for j in range(100, 0):
x=x-j
print('x=')

if __name__ == '__main__':
p1 = multiprocessing.Process(target=raufrechnen, args=())
p2 = multiprocessing.Process(target=runterrechnen, args=())
p1.start
p2.start
print(z, x)
Zuletzt geändert von Micki am Freitag 20. März 2020, 12:28, insgesamt 2-mal geändert.
Sirius3
User
Beiträge: 18227
Registriert: Sonntag 21. Oktober 2012, 17:20

Solange Du Deinen Code hier nicht zeigst, können wir hier nur raten, was Du brauchst. Das ist nicht sehr effizient.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Ich weiß nicht, wie ich einen Screenshot im Raspi mache und poste. Ich hab das Script oben kopiert und in der Vorschau stimmten die Einrückungen noch (sry)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du luegst dir in die Tasche, wenn du denkst, die Trennung von GPIO und MIDI Verarbeitung in getrennten Prozessen wuerde dir hier irgendetwas einbringen. Das grundlegende Problem besteht darin, dass die Ergebnisse des einen Prozesses in den anderen muessen. Und damit gibt es zwangsweise einen Moment, wo der MIDI-Prozess in einem *Thread* die Daten aus der Queue des anderen Prozesses entgegennimmt. Nur weil das unter der Haube durch multiprocessing geschieht, ist diese Eigenschaft nicht magisch verschwunden. In der Summe bleiben also alle vermeintlich vermiedenen Nachteile bestehen, nur jetzt kommt noch erhoehte Gesamtlast hinzu, denn es sind ja ploetzlich mehrere Prozesse im Spiel.

Darueber hinaus gibt es natuerlich noch einen ganzen Zoo von Gruenden, warum du durch die Verwendung von Python hier sowieso Grenzen in der Echtzeitigkeit deines Systems hast. Python allokiert permanent Speicher, nutzt Kernel-Locks zur Synchronsation, kennt keine lock-Freien Datenstrukturen (die man normaleirweise fuer einen solchen Transport von einem Subsystem ins andere nutzen wuerde), hat das GIL, etc. Die daher fuer sowas geeignetes Sprachen sind C, C++ und Rust. Python ist per Definition nicht echtzeitfaehig.

Aber das ist nur die Meinung von jemanden, der bei einem der bekanntesten Hersteller fuer Musik-Soft- und Hardware in der Embedded-Programmierung arbeitet :twisted: Du weisst es sicher besser.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

Ich bin Euch total dankbar, dass Ihr Euch meines Problems annehmt.
Ich habe ja die beiden Prozesse am laufen. Leider nur getrennt.

Jetzt möchte ich erlernen, wie man 2 Prozesse startet.

Der Midi-Datenfluss hat nichts mit den GPIOs zu tun. Das läuft über USB.
Die GPIOs liefern nur Parameter, die später verarbeitet werden (z.B. Transpose). Es schreibt also nur 1 Prozess, der andere liest nur.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

__deets__ hat geschrieben: Freitag 20. März 2020, 10:53 Ok, wenn du es besser weisst - hier: https://docs.python.org/3.7/library/mul ... ssing.html Da steht alles drin, vom Start mehrere Prozesse, und Kommunikation zwischen diesen, bis hin zu geteiltem Zustand.

Was daran KONKRET ist dir unklar? Hast du damit experimentiert? Was geht nicht?
Danke, schau ich mir an. Sieht gut aus.
Nur, da ich noch sehr neu im Thema bin, werde ich dazu Zeit brauchen ....
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Micki: Das ist die Dokumentation zum `multiprocessing`-Modul. Wie kann man die erst nach einem Hinweis darauf lesen wenn man das Modul verwenden will‽ 😲

Die API von `Process`-Objekten ist der von `Thread`-Objekten aus dem `threading`-Modul nicht unähnlich.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

__blackjack__ hat geschrieben: Freitag 20. März 2020, 13:05 @Micki: Das ist die Dokumentation zum `multiprocessing`-Modul. Wie kann man die erst nach einem Hinweis darauf lesen wenn man das Modul verwenden will‽ 😲

Die API von `Process`-Objekten ist der von `Thread`-Objekten aus dem `threading`-Modul nicht unähnlich.
Das liegt daran, dass ich neu bin und mein Englisch Lücken hat. Ich habe aber jede Menge Informationen bereits verarbeitet. Das wird noch werden. Solange ich noch lerne, erbitte ich Gnade von den Fortgeschrittenen und Meistern.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

__blackjack__ hat geschrieben: Freitag 20. März 2020, 13:05 @Micki: Das ist die Dokumentation zum `multiprocessing`-Modul. Wie kann man die erst nach einem Hinweis darauf lesen wenn man das Modul verwenden will‽ 😲

Die API von `Process`-Objekten ist der von `Thread`-Objekten aus dem `threading`-Modul nicht unähnlich.
Das liegt daran, dass ich neu bin und mein Englisch Lücken hat. Erbitte Gnade bei den Fortgeschrittenen und Meistern.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Fortgeschrittenen und Meister wuerden sich eher wuenschen, dass du die Gruende fuer dein Vorgehen hinterfragst, weil es nicht zielfuehrend ist. multiprocessing ist cool, hat aber nicht die Eigenschaften, die DU dir davon versprichst. Es in einem Prozess zu loesen ist fuer dich einfacher, und erzielt genauso gute Ergebnisse.
Micki
User
Beiträge: 34
Registriert: Freitag 20. März 2020, 09:17

__deets__ hat geschrieben: Freitag 20. März 2020, 14:50 Die Fortgeschrittenen und Meister wuerden sich eher wuenschen, dass du die Gruende fuer dein Vorgehen hinterfragst, weil es nicht zielfuehrend ist. multiprocessing ist cool, hat aber nicht die Eigenschaften, die DU dir davon versprichst. Es in einem Prozess zu loesen ist fuer dich einfacher, und erzielt genauso gute Ergebnisse.
Die Gründe für mein Vorgehen liegen z.B. darin, dass einer der Knöppe auch ein Songfile startet, der unabhängig vom Midi-Datenstrom laufen soll. Und zwar gleichzeitig. Nicht scheinbar gleichzeitig.

Wie dem auch sei. Es ist schade, dass mir niemand einfach meine Frage beantwortet, obwohl hier ja Meister sind. Ich arbeite mich gerade durch die Python-Doks. Bisher habe ich aber nicht hinbekommen, dass 2 Prozesse gleichzeitig laufen. Die werden immer nur seriell abgearbeitet. D.h., erst der obere Teil, dann der untere.

Trotzdem danke für Deine Antworten. Alles wird gut. :D :geek:
Sirius3
User
Beiträge: 18227
Registriert: Sonntag 21. Oktober 2012, 17:20

Dass ein wirkliches Gleichzeitig auf einem Raspi ohne Echtzeitbetriebssystem nicht geht, wurde Dir ja schon gesagt, was Du aber anscheinend ignorierst.
Bisher hast Du nur nicht-funktionierenden Python-Code gezeigt, der mit Deinem realen Problem so viel zu tun hat, wie eine Anleitung zur Fahrrad-Reparatur, wenn man ein kaputtes Flugzeug hat.
Läßt sich aber auch trivial in ein funktionierendes Beispiel umschreiben:

Code: Alles auswählen

import multiprocessing
import time

def raufrechnen():
    for i in range(0, 100):
        print('z=', i)
        time.sleep(0.1)

def runterrechnen():
    for j in range(100, 0, -1):
        print('x=', j)
        time.sleep(0.1)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=raufrechnen, args=())
    p2 = multiprocessing.Process(target=runterrechnen, args=())
    p1.start()
    p2.start()
Antworten