Zugriff auf Eigenschaften einer Klasseninstanz mit mehreren Prozessen

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
Martin1986
User
Beiträge: 13
Registriert: Freitag 4. September 2020, 10:13

Hallo liebe Forenmitglieder,
ich bin vor einiger Zeit auf das Modul "multiprocessing" gestoßen und versuche gerade eine Funktion einer Klasseninstanz mit mehreren Prozessen zu starten. Dabei wird auf eine Eigenschaft der Instanz lesend zu gegriffen und eine zweite Eigenschaft geschrieben (String wird erweitert!). Wie schon erwähnt eben mit mehreren Prozessen
Wie zu erwarten funktioniert das nicht!

Daher meine Fragen an euch:
  • Ist die Herangehensweise überhaupt die Richtige?
  • Falls nein, wie würdet ihr das Problem lösen?
  • Falls ja, was fehlt noch um ein lauffähiges Script zu bekommen? Bzw. wo kann ich das nachlesen?
Ich habe ein kurzes übersichtliches MInimalbeispiel gemacht um die Problematik zu verdeutlichen!

Code: Alles auswählen

# -*- coding: utf-8 -*-
from multiprocessing import Process

class Test_Klasse:
    def __init__(self):
        # Eigenschaft welche erweitert wird
        self.erg_string = "Ergebnisse:\n"
        # lesender Zugriff auf Eigenschaft
        self.b_dict = {"A":1, "B":2, "C":3, "D":4, "E":5, "F":6}

    def gen_diff(self, zahlen_dict):
        '''
        Bildet Differenz aus zwei Zahlen
        und speichert Ergebnis als String in self.erg_string
        '''
        for i in zahlen_dict:
            erg = zahlen_dict[i] - self.b_dict[i]
            self.erg_string = self.erg_string + "{} = {}\n".format(i, str(erg))
        

if __name__ == "__main__":

    test_Dict_liste = [{"A":15, "B":14, "C":13}, {"D":12, "E":11, "F":10}]
    procs = []

    T = Test_Klasse()
##### Achtung, Quellcode funktioniert nicht #######   
    for t_dict in test_Dict_liste:
        proc = Process(target=T.gen_diff, args=(t_dict,))
        procs.append(proc)
        proc.start()

    for proc in procs:
        proc.join()
###################################################           
    print "Juhu!"
Vielen Dank für eure Hilfe schon im Vorraus!
Grüße Martin
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

Der _ in Test_Klasse ist unnötig, da durch die groß-klein-Schreibung schon die einzelnen Bestandteile lesbar sind. Benutze keine Abkürzungen und Typen haben in Variablennamen auch nichts verloren: erg_string -> ergebnis, oder b_dict -> buchstaben? Was hat die Methoden gen_diff mit Genen zu tun? `i` ist ein sehr schlechter Name für einen Schlüssel, der einen Buchstaben repräsentiert.
Alles was unter if __name__ steht, sollte in eine Funktion wandern, die man üblicherweise main nennt.
Das Problem ist nicht für Multiprocessing geeignet, da Du von überall auf die selbe Datenstruktur zugreifst und veränderst.
Was Du eigentlich willst, ist gar keine Klasse, sondern eine Funktion, die aus zwei übergebenen Wörterbüchern einen String erzeugt.
Das kann man dann unabhängig für viele Wörterbucher per multiprocessing.Pool.map umwandeln lassen und im Hauptprogramm per join zu einem String verbinden.

Code: Alles auswählen

from multiprocessing import Pool
from functools import partial

def build_string(items_a, items_b):
    return "".join(f"{key} = {items_a[key] - items_b[key]}\n" for key in items_b)

def main():
    main_items = {"A":1, "B":2, "C":3, "D":4, "E":5, "F":6}
    test_items = [{"A":15, "B":14, "C":13}, {"D":12, "E":11, "F":10}]
    
    pool = Pool()
    ergebnis = "".join(pool.map(partial(build_string, main_items), test_items))
    print(ergebnis)

if __name__ == '__main__':
    main()
Was willst Du eigentlich machen? Was wäre Deine wirkliches Problem, das Du per multiprocessing zu lösen versuchst?
Martin1986
User
Beiträge: 13
Registriert: Freitag 4. September 2020, 10:13

Hallo Sirius3, danke für deine Antwort.
Ok, dass mit den Bezeichnungen werde ich in Zukunft beachten!
Naja, ich habe einen bestehenden Quelltext mit eine Klasse, die die Aufgabe hat, Dataframes (Pandas) voneinander zu subtrahieren (100000+ mal) (und andere Modifikationen der Dataframes) . Das klappt sehr gut und zuverlässig. Leider dauert das ganze eben eine gewisse Zeit. Daher kam mir die Idee, das ganze mit dem multiprocessing-Modul zu beschleunigen.
An deinen Vorschlag mit der map()-Funktion habe ich auch schon gedacht, aber das würde natürlich eine komplette Neuprogrammierung des Tools bedeuten. Mein Ziel war und ist es natürlich, den Änderungsaufwand so gering wie möglich zu halten (Never touch a running system!).

Dass ich mit mehreren Prozessen auf die selbe Datenstruktur zugreife ist mir bewusst. Ich bin im multiprocessing-Modul nicht wirklich fit und dachte es gibt irgendeine Möglichkeit.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du eine geteilte Datenstruktur haettest, muesste die wiederum muehselig vor gleichzeitigem Zugriff geschuetzt werden, weil es sonst zu Fehlern in der Berechnung kommt. Bringt also alles nix. Wenn du da Geschwindigkeit gewinnen willst, ist so etwas wie map/reduce tatsaechlich eine sehr gute Wahl.
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

Große Matrixrechnungen können mit numpy+mkl schon automatisch auf mehrere Kerne per Multithreading verteilt werden. Das ist deutlich effizienter, da sich die Threads den Speicher teilen und deutlich einfacher zu programmieren, weil man nichts extra machen muss.

Klassen sollte man nur benutzen, wenn sie auch für das Programm sinnvoll sind. Dass man ein Tool mehrmals während der Entwicklung umstrukturieren muß, ist auch nichts ungewöhnliches. Das ist meist schnell gemacht und hilft, die Komplexität nicht unnötig wachsen zu lassen.

Wenn Du den tatsächlichen Code hier postest, können wir auch viel besser helfen.
Martin1986
User
Beiträge: 13
Registriert: Freitag 4. September 2020, 10:13

Vielen Dank für eure Antworten.
Ihr habt mir deutlich gemacht, dass es so nicht funktionieren wird.
Ich werde also nicht drumherum kommen, das Ganze umzubauen.
Einen schönen Tag noch.

Viele Grüße Martin
Antworten