Seite 1 von 1

on button clicked multiprocessing threading

Verfasst: Dienstag 13. Juni 2017, 16:06
von martinjo
Hallo

Ich arbeite mit Python 2.7 und Gtk3

Hier möchte ich gerne eine Funktion über einen Gtk.Button aufrufen. Diese soll dann im Hintergrund laufen. Ich habe es schon mal zum laufen bekommen aber inzwischen leider nicht mehr. Es wird bei beiden Beispielen die gesamte Anwendung blockiert.

Code: Alles auswählen

from threading import Thread
from multiprocessing import Process 


# Beispiel 1 -  threading
button.connect("clicked", self.on_run_background_process_clicked)
def on_run_background_process_clicked(slelf, widget):
        Thread(target=Shipping().add_shipping_numbers()).start()


# Beispiel 2 - multiprocessing
button.connect("clicked", self.on_run_background_process_clicked)
def on_run_background_process_clicked(slelf, widget):
    a = Process(name='add_shipping_numbers', target=Shipping().add_shipping_numbers())
    a.start()
    a.join()
Es wäre noch super eine Möglichkeit zu haben, zu prüfen ob ein Prozess schon läuft, denn 2x sollte der natürlich nicht laufen um Probleme mit Überschneidung zu vermeiden.

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 13. Juni 2017, 16:19
von BlackJack
@martinjo: Wenn ich mal raten müsste, dann ist gibt die `add_shipping_numbers()`-Methode *nicht* die Methode oder Funktion zurück die ausgeführt werden soll, sondern sie *ist* die Methode die ausgeführt werden soll‽ Dann darfst Du sie nicht aufrufen und den Rückgabewert an `Thread()` oder `Process()` übergeben, sondern die Methode selbst. Aufgerufen wird sie dann ja von `Thread`/`Process`.

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 13. Juni 2017, 16:33
von martinjo
Vielen Dank, Problem gelöst :-)

Eine Frage noch, kann ich auch verhindern, dass ein Prozess mehrmals parallel läuft? Ich kann ja sowohl "Thread" als auch "Process" einen Namen geben, kann man nicht anhand von diesem prüfen ob der Prozess bereits läuft?

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 13. Juni 2017, 16:51
von BlackJack
@martinjo: Ich würde mir das einfach merken, denn normalerweise deaktiviert man bei solchen Sachen ja auch den Startbutton, damit der Benutzer das gar nicht noch einmal starten *kann* solange es läuft.

Re: on button clicked multiprocessing threading

Verfasst: Mittwoch 14. Juni 2017, 15:16
von martinjo
Hallo, da habe ich doch dann das selbe Problem, dass ich feststellen muss wann ein Prozess zu Ende ist um den Knopf wieder frei zu geben.

Re: on button clicked multiprocessing threading

Verfasst: Mittwoch 14. Juni 2017, 15:46
von BlackJack
@martinjo: Über das Ende informiert doch hoffentlich der Prozess. :-) Ich würde ja `concurrent.futures` verwenden. Da kann man leicht zwischen Thread und Prozess wählen, und eine Rückruffunktion registrieren wenn die Aufgabe durch ist, egal ob wegen Ergebnis/normales Ende oder wegen Ausnahme.

Re: on button clicked multiprocessing threading

Verfasst: Mittwoch 14. Juni 2017, 17:09
von __deets__
@BlackJack: ist diese Rueckruffunktion nicht in einem extra Thread? Wenn ja, muss die natuerlich einen geeigneten Weg nutzen mit der GUI zu reden.

Re: on button clicked multiprocessing threading

Verfasst: Mittwoch 14. Juni 2017, 17:20
von BlackJack
@__deets__: Jup, das muss man berücksichtigen.

Re: on button clicked multiprocessing threading

Verfasst: Freitag 16. Juni 2017, 16:28
von martinjo
Hallo
Ich hab es leider nicht ganz verstanden. Der Prozess informiert mich nicht wenn er zu ende ist.

Re: on button clicked multiprocessing threading

Verfasst: Freitag 16. Juni 2017, 17:06
von BlackJack
@martinjo: Sollte er aber. :-)

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 20. Juni 2017, 19:30
von martinjo
In wie fern meldet er sich denn zurück?

Ich starte einen Prozess, der läuft ja im Hintergrund. Wie z.B. unten das Beispiel mit Process aber ohne join dann.

Ich kann jedoch prüfen ob der Prozess läuft mit p.is_alive()

Meine Lösung inzwischen ist nicht schön, trotzdem möchte ich Sie zumindest vorstellen:

Code: Alles auswählen

# Beispiel - multiprocessing
button.connect("clicked", self.on_run_background_process_clicked)

def on_run_background_process_clicked(slelf, widget):
        try:
            if self.p.is_alive():
                logger.warning("process is already running")
                return
            else:
                raise
        except:
                pass
    self.p = Process(name='add_shipping_numbers', target=Shipping().add_shipping_numbers())
    self.p.start()

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 20. Juni 2017, 19:48
von __deets__
Bits du sicher, das das geht? Mir sieht das so aus als ob du die add_shipping_numbers gleich aufrufst - sind ja Klammern hinter. Das wurde ja schon besprochen...

Und statt es zu machen wie du wäre ein timer der is_alive prüft & dann den button enabled besser. Noch besser das von BJ angesprochene concurrent.futures.

Re: on button clicked multiprocessing threading

Verfasst: Dienstag 20. Juni 2017, 21:22
von BlackJack
@martinjo: Das war wohl missveständlich von mir: Mit sollte er aber meinte ich nicht das er das schon machen sollte, sondern das er das besser machen sollte, also Du das so programmieren solltest das er das macht. Das wäre nämlich die benutzerfreundliche Variante. Also zum Beispiel ein `concurrent.futures.Future` das am Ende über eine Rückruffunktion informiert das das Ergebnis da ist. Auch wenn der Prozess kein Ergebnis hat, ist die Information das es *da* ist, ja auch eins. Ausserdem kann man da auch erfahren ob die asynchrone Funktion tatsächlich durchgelaufen ist, oder ob sie mit einer Ausnahme endete. Die Information kann ja auch nützlich sein.

``raise`` ohne Argument? Das geht nicht. Also das geht schon — es löst einen `TypeError` aus der sagt das ``raise`` ohne Argument (also implizitem `None`) nicht geht. :-D Deswegen ist ja wahrscheinlich auch das unsinnige und unsägliche ``try``/``except`` um diesen Test. Ich hoffe mal das ist da nicht nur deswegen weil das `p`-Attribut vor dem ersten Start `None` ist und man darauf kein `is_alive()` aufrufen kann. *Das* kann man ja schliesslich einfach prüfen.

Code: Alles auswählen

    def on_run_background_process_clicked(self, widget):
        if self.process and self.process.is_alive():
            logger.warning('process is already running')
        else:
            self.process = Process(target=Shipping().add_shipping_numbers)
            self.process.start()

Re: on button clicked multiprocessing threading

Verfasst: Donnerstag 22. Juni 2017, 11:02
von martinjo
Hallo

Ja, die Klammern müssen weg, versehentlich aus dem ersten Beispiel übernommen.

@BlackJack
self.process existiert ja erst nachdem die Funktion das erste mal aufgerufen wurde. Da das Programm aber viele Funktionen hat möchte ich ungern self.process bereits beim initialisieren setzen.

Re: on button clicked multiprocessing threading

Verfasst: Donnerstag 22. Juni 2017, 11:44
von BlackJack
@martinjo: Das ist ein guter Weg sich ins Knie zu schiessen — Klassen mit vielen Methoden die jeweils ihre eigenen Attribute erzeugen können. Nach der Abarbeitung von `__init__()` sollte eine Klasse alle Attribute besitzen und in einem benutzbaren Zustand sein. Das Du die Klasse Programm nennst und die Methode Funktion, und das es davon viele gibt, legt den Verdacht nahe das Du da eigentlich einen Haufen Funktionen mit ``global`` in eine Gott-Klasse verschoben hast. Dadurch verschwindet zwar das ``global``-Schlüsselwort, aber das eigentliche, zugrunde liegende Problem damit bleibt bestehen.