Wie mit Threading Spinner vermeiden ?

  • Hallo


    Ich hab hier mehrere Fälle, wo es trotz Threading zu Spinnern kommt.

    Ist das tatsächlich so, dass auch bei Threading Spinner kommen oder mach ich da was falsch ?


    Hier mal ein Beispiel für eine Klasse zum Ausführen von Consolen-Befehlen (im konkreten Fall für "apt-get update").

    Trotz Threading kommen hier kurz Spinner, wenn der Befehl mal etwas länger ausgeführt wird.

    Die Ausführung dauert meist so um die 5 Sekunden (mal ohne und mal mit kurzem Spinner).

    Ok, ich hab hier inzwischen eConsoleAppContainer in Verwendung, wo es Dank der callback-Funktion keine Spinner gibt.

    Mich interessiert das aber trotzdem mal ganz allgemein, wie man normale länger andauernde Funktionen in einen Thread auslagert, um Spinner zu vermeiden.

    So sieht dann der Aufruf der Class aus:

    Das gleiche Problem hab ich mit dem pzymail.

    Da dauert das imap-Login auch manchmal etwas länger, was dann auch zu unschönen Spinnern führt.


    Hab mir die Login-Funktion schon als SimpleThread-Aufruf umgebaut. Aber ganz ohne Spinner klappt das da auch nicht.


    Aufruf:

    Code
    def _imap_connect_thread(self, readonly = True):
    import Queue
    from Plugins.SystemPlugins.Toolkit.SimpleThread import SimpleThread
    out_queue1 = Queue.Queue()
    t = SimpleThread(self._imap_connect(out_queue1, readonly=readonly))
    t.start()
    t.join()
    return out_queue1.get()

    Login-Funktion (stark verkürzt dargestellt):

    Code
    def _imap_connect(self, out_queue1, readonly=True):
    imapObject = IMAP4_SSL(host=popserverhost, port=popserverport)
    (retcode, capabilities) = imapObject.login(popuser,poppass)
    imapObject.select(mailbox='INBOX', readonly=readonly)
    self.imapObject = imapObject
    out_queue1.put(imapObject)
    return

    Auch hier dauert das .login bzw. das .select mal ein paar Sekunden, was dann mit den unschönen Spinnern begleitet wird.


    Was muss man tun, um die Spinner zu vermeiden?

    Das ganze läuft ja bei beiden Fällen im Hintergrund, so dass es nicht stört, wenn die Abfrage etwas länger dauert.

    Was nur stört, sind die Spinner, weil man jedesmal denkt, die Box hat sich aufgehängt ;)

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Hm eigentlich können nur Spinner kommen wenn du den Hauptprozess auf irgendwas warten lässt.


    Ich nutze das nur mit thread.start_new_thread aber das sollte auch egal sein, Rückmeldung gebe ich immer mit reactor.callFromThread

  • Das Plugin ruft ja an einer Stelle das Thread-Login mit imapobject = self._imap_connect_thread(readonly=True) auf.


    Dort wartet es dann vermutlich trotz Thread.

    Wäre jetzt zumindest mein Gedanke.


    Müsste ich also den Aufruf ohne eine Rückgabe machen und dann das Plugin über eine gesonderte Funktion

    aus dem Thread-Return neu anstupsen (weitermachen lassen) ?

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Ja siehe mein Beispiel das funzt dann auf jeden Fall wenn du das mit callFromThread zurückmeldest wenn abgeschlossen. Das Problem wird bei deinem return value liegen, daher wartet der Hauptprozess dann auf Rückmeldung deiner Funktion.

  • Ok, im pzymail liegt es tatsächlich an den return_values beim Aufruf einer länger andauernden Funktion - return_value = Funktion(…).

    Problem ist dort nur, dass da sehr verschachtelte Funktionsaufrufe stattfinden, so dass bei einem CallBack mit callFromThread plötzlich die Kette der returns abreißt.


    Muss ich mal schauen, ob ich die Zielfunktion für das Return noch mitgeben kann ;)

    (beim imap_delete_mail hat das testweise schon geklappt, wobei es da nichtmal so stören würde)


    Also hängt es im o.g. 1. Beispiel vermutlich auch am return_value in self.process = subprocess.Popen(…).

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Hallo


    ich nutze für kleine Zeitfresser defertoThread(). Ist simpel und effektiv.

    Code
    def delSelSong(self, fn):
    deferToThread(JCdeleteSongfileonThread, fn).addCallback(self.finishedJCdeleteSongfileonThread,).addErrback(self.errorJCdeleteSongfileonThread,)

    Die meisten Probleme macht man sich selber!

  • ok, aber ohne thread.join() funktioniert das hier nicht ;)

    Ich hatte gehofft, das bei Threads generell keine Spinner kommen, aber das ist wohl nicht so.


    Grundsätzlich sind hier wohl immer Lösungen mit einem Callback zu bevorzugen.

    Da läuft das ganze dann ja tatsächlich im Hintergrund.


    Ich war eben auch gerade beim pzymail auf der Suche nach einer Variante, die man unkompliziert nachträglich integrieren kann.

    Habe jetzt aber festgestellt, dass im pzymail ein Aufruf von self._imap_connect_thread() in anderen Funktionen auch Spinner verursacht, obwohl hier kein return_value im Aufruf ist.

    Es hängt dann im Code trotz Thread beim (retcode, capabilities) = imapObject.login(popuser,poppass) für wenige Sekunden.

    Da der Thread aber auch mit .join() läuft, kommt es hier wohl wieder zu den Spinnern.


    Kann man für einen Thread die Spinner auf einen Zeitwert X festlegen ?

    Also nicht schon nach 5 Sek sondern z.B. erst nach 10 Sek. die Spinner?

    Die problematischen Funktionen laufen hier immer so 5-6 Sekunden. Da schlagen dann die Spinner meist zu früh an ;)

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Auch dein Thread läuft async im Hintergrund - nur blockierst du ja den main Thread mit dem join.

    (Da kannst dir den Thread gleich sparen!)


    Verwende einfach callbacks wenn die Aktion abgeschlossen ist!

    Allerdings darfst du keine E2 Methoden über einen anderen thread aufrufen.

    Also entweder pollen mit einem eTimer oder callFromThread von Twisted verwenden.


    Code
    from twisted.internet import reactor
    reactor.callFromThread(your_callback)

    In der AMS Source/MovieCache.py kannst dir das schön anschauen!

    Da werden Trhead und Event verwendet - kann man immer brauchen sowas ;)

    Edited once, last by CMikula ().

  • Ja, das hatte dhwz oben schon empfohlen, was auch funktioniert ;)


    Allerdings habe ich echte Probleme, das in den pzymail-Code zu integrieren.

    Da gibt eine Funktion die über einen Timer (z.B. alle 5min) die Email-Accounts abruft.


    Hier mal schematisch stark verkürzt diese Funktion:

    Die Funktion ruft sich selber immer wieder auf, bis alle Mail-Accounts abgearbeitet sind (erledigte werden mit Pop aus der Liste gelöscht).

    Am Ende wenn alle Accounts aktualisiert wurden, wird eine weitere Folge-Function (self.cb) aufgerufen.


    Die Spinner kommen in der Mitte bei self._imap_connect_thread() (der Code dazu ist in Post #1)

    Dadurch, dass das ganze hier in einer Schleife läuft, habe ich gerade keine Idee, wie ich da ein Callback einbauen kann :/

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Ich hatte gerade mal versucht, diese gesamte o.g. Funktion mit einem Thread aufzurufen:

    Code
    def update_allAccounts_timer_thread(self,state=0):
    from twisted.internet.threads import deferToThread
    deferToThread(self.update_allAccounts_timer, state)

    Da gab es dann allerdings einen GS:

    FATAL!: addTimer must be called from thread 21576 but is called from thread 21692


    Da kommt dann wohl irgendwas durcheinander, weil addTimer ja gar niht vom pzymail ist ;)

    Meinst du, dass das self.cb() daran Schuld ist und ich dieses dann in den CallBack packen müsste ?


    Wenn die Function sich über einen Timer (1 sek) immer selbst aufruft, geht das beim Thread überhaupt?

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)

  • Dein Crash kommt daher, dass Du eTimer ausführen willst in einem Thread. ;)

    Enigma2 ist nicht threadsicher, d.h. Du darfst in einem Thread niemals Enigma2-Objekte ansprechen

    (siehe deine Crash Meldung).


    Du musst also immer aus dem Thread wieder nach Enigma2 gelangen, wenn Du E2 Objekte ansprechen willst.

    Es gibt da viele Wege, die nach Rom führen. Führe in Deinem Fall beispielsweise deferToThread mit einem addCallback aus, um wieder in die MainLoop von E2 zu gelangen.

  • Hab jetzt eine Lösung gefunden, die gesamte Funktion, wo alle Accounts regelmäßig im Hintergrund geprüft werden, mit einem Thread mit deferToThread aufzurufen ;)

    Die dort integrierten eTimer habe ich ins anschließende Callback gepackt.

    Bis jetzt läuft es. Bisher auch ohne Spinner.


    Danke an alle hier für die Hilfe :thumbsup:


    Nun mal beobachten, ob die Änderungen nicht doch bisher unbekannte Fehler verursacht haben :/


    Wird das Callback eigentlich immer ausgelöst oder kann es auch nur ein addErrback geben?

    Wenn letzteres, dann müsste ich da auch noch die eTimer reinpacken, da sonst die Prüfung im Hintergrund bestimmt nicht fortgeführt wird.

    Gruß Sven (aka Dreamy)


    (DM920 mit unstable OE2.5 DMM)