Wie kann ich laufenden Thread neu starten?

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.
dkell
User
Beiträge: 12
Registriert: Mittwoch 12. März 2008, 09:40

Hallo

Ich habe ein Programm, welches aufgrund einer Paramtertabelle in einer Datenbank verschiedene Threads startet (Beispielhaft unten vereinfacht dargestellt). Daneben gibt es noch ein Web-Gui (ebenfalls in einem eigenen Thread), welches die Konfiguration der Parametertabelle zulässt.

Nun möchte ich, wenn über das Webgui ein Eintrag in der Tabelle geändert wird, der enstprechende Thread neu gestartet wird. Irgendwie müsste ja dann der entsprechende laufende Thread beendet und neu gestartet werden, aber wie dies vom Hauptprogramm aus bewerkstelligen?

Vielen dank für Infos

Dani

Code: Alles auswählen

import threading
import time

# [ID, Programm, Paramter]
db = [[1, 1, 50], [2, 2, 20]]


class TestThread1 ( threading.Thread ):

    def __init__ ( self, tsleep ):
        threading.Thread.__init__( self )
        self.tsleep = tsleep

    def run ( self ):
        i = 1
        while 1:
            time.sleep(self.tsleep)
            print "Thread Programm 1: i = %s" % (i)
            i = i + 1


class TestThread2 ( threading.Thread ):

    def __init__ ( self, tsleep ):
        threading.Thread.__init__( self )
        self.tsleep = tsleep

    def run ( self ):
        i = 1
        while 1:
            time.sleep(self.tsleep)
            print "Thread Programm 2: i = %s" % (i)
            i = i + 1
            

Threads = []

for x in db:
    if x[1] == 1:
        Threads.append ( TestThread1(x[2]) )
    elif x[1] == 2:
        Threads.append ( TestThread2(x[2]) )


# Threads starten
for t in Threads:
    t.start()


Zuletzt geändert von Anonymous am Montag 25. Oktober 2010, 09:12, insgesamt 1-mal geändert.
Grund: Syntax-Highlighting aktiviert.
BlackJack

@dkell: Threads kann man weder von aussen beenden noch kann man den Thread selbst erneut laufen lassen. Du musst also den Threadcode so schreiben, dass er selber bei bestimmten Bedingungen mit einem "Neustart" reagiert und das ein Neustart der Berechnung keinen Neustart des Threads erfordert.

Also zum Beispiel anstelle des ``while 1:`` auf eine Bedingung prüfen, welche der GUI-Thread von aussen beeinflussen kann und das ganze selbst dann vielleicht wieder in eine "Endlosschleife" einbetten, wenn der Thread nicht abgebrochen werden soll sondern mit anderen Argumenten weiter rechnen soll.

Als "Endlosschleife" ist ``while True:`` IMHO übrigens lesbarer.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

Hallo,

ich habe irgendwie das gleiche Problem!
Ich möchte mehrere Threads neu starten.
Ich habe eine Gui erstellt mit Buttons einen für start und den anderen zum beenden.
Das mit dem beenden funktioniert ganz gut, aber ich kann nicht neu starten. Weil die Threads noch aktiv sind.
ich hae insgesamt 4 threads.
Alle beende ich indem ich nicht mehr in meine While schleife gehe.
While se.Open () and self.keeprunning:

Zum beenden des Programms druecke ich den Button beenden, mit diesem self.keeprunning auf False gesetzt wird.

Wie kann ich alle Threads neu starten, wenn ich danach wieder auf Start drücke.

Vielen Dank
deets

Campionissimo hat geschrieben: Wie kann ich alle Threads neu starten, wenn ich danach wieder auf Start drücke.
Auch ein Jahr spaeter bleibt BlackJacks antwort gueltig. Es geht nicht. Du darst deine Threads entweder nicht beenden, oder du startest neue. Einen alten "wiederbeleben" geht nicht - und ist ja auch semantisch von einem neuen nicht wirklich zu unterscheiden.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

Also nur neue !
Was ist wenn jemand 100 mal auf den button beenden drückt und auch wieder startet.
Dann hab ich 400 Threads aktiv.
Dann ist ja das system überlastet oder nicht.
BlackJack

@Campionissimo: Ich denke wenn der Benutzer auf „Beenden” drückt, dann beenden sich die Threads!? Sollten sie jedenfalls, denn sonst müllst Du Dir das System tatsächlich mit Threads zu.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

Beenden tun sich die Threads nicht. Sie führen keine Aktionen mehr aus. Sie verlaufen irgendwohin.
Aber das müsste docj irgendwie funktionieren. Bei anderen Oberflächen kann man doch auch Beenden und wieder starten.
Oder soll ich ein wait einführen ?
Wenn er die Beenden Taste drückt wartet das System einfach und wenn wieder auf start gedrückt wird wird das wait aufgehoben.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

Ich dachte Threads kann man mit einem Tastendruck nicht beenden!
BlackJack

@Campionissimo: Threads „verlaufen” nicht „irgendwohin”. Es wird der Code ausgeführt den Du geschrieben hast. Entweder läuft der, oder er ist am Ende und damit auch der Thread. Man kann Threads nicht von *aussen* beenden, deshalb muss der Thread selbst dafür sorgen das er sich beendet.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

In den verschiedenen Threads habe ich diese Funktion:

while self.se.isOpen() and self.KeepRunning:

In meiner GUI Oberfläche drücke ich die Beenden Taste. Dann wird self.KeepRunning auf False gesetzt.
Und somit nichts mehr ausgeführt, da mein ganzes Programm in while steht.
Aber beendet habe ich die Threads nicht, weil wenn ich wieder start drücke heisst das die Threads noch aktiv sind.
Und wie kann der Thread selber dafür sorgen, dass er sich beendet?

Vielen Dank
BlackJack

@Campionissimo: Wenn der Code nach ``while``-Schleife abgearbeitet ist (sofern da überhaupt welcher steht) ist der Thread beendet. Du bekommst anscheinend eine Fehlermeldung im weiteren Verlauf beim Druck auf „Start”? Dann zeig die doch mal.
LivingOn
User
Beiträge: 33
Registriert: Montag 11. August 2008, 07:53

Die Sache mit while 1 und time.sleep() in einem Thread, ist meinen Erfahrungen nach eher suboptimal. Ich versuche wenn möglich immer mit threading.Timer() zu arbeiten. Die haben den Vorteil, dass sie jederzeit (auch von außen) abbrechbar sind und sich problemlos wieder starten lassen. Der folgende Code entstammt einem meiner Projekte und ist auf das Nötigste reduziert:

Code: Alles auswählen

import threading

class Worker(threading.Thread):
    def __init__(self):
        super(Worker, self).__init__()
        self.timer    = None
        self.runflag  = True
        self.lock     = threading.Lock()

    def run(self):
        self.lock.acquire()
        if self.runflag:

            #
            # hier können die "wichtigen" Aufgaben erledigt werden ;-)
            #

            # nach 10 Sekunden wird self.run gestartet
            self.timer = threading.Timer(10, self.run)
            self.timer.start()
        self.lock.release()

    def stop(self):
        self.lock.acquire()
        self.runflag = False
        if self.timer:
            self.timer.cancel()
        self.lock.release()
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

Danke erstmal für deine Antwort. Schaut schon mal gut aus.
Wegen der if bedingung:
Ich brauch die while schleife, da ich einen seriellen Port einlese.
Wenn er nicht mehr offen sein sollte, werden meine Aktionen nicht mehr ausgeführt.
Wenn ich jetz if habe, dann schaut er nur am Anfang nach. Es kann nicht mehr überprüft werden während des Programms ob der Port offen ist.

Time.sleep() benutze ich um den thread warten zu lassen, während der eine time.sleep hat kann der andere Thread weiter machen also quasi parallel.

Funktioniert das ganze auch wenn ich den Thread schliesse und dann wieder öffne, also das heisst er ist restartable ?
Wenn ich die start und das beenden mit buttons mache ohne das ich das alles neu compiliere.

Wenn ich falsch liege bitte sagen. Ich suche nur einen weg, damit ich die Threads immer wieder neu starten und beenden kann.
Campionissimo
User
Beiträge: 102
Registriert: Montag 28. März 2011, 07:50

@ BlackJack
hier die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Ordner\Test\GUI.py", line 991, in buttonClick
    self.omodem.start()
  File "C:\python25\lib\threading.py", line 436, in start
    raise RuntimeError("thread already started")
RuntimeError: thread already started
Wie du sagst beende ich anscheinend doch die Threads. IM jeden Thread habe ich eine While schleife.
"While self.KeepRuninng:"
Wenn ich den Button beenden drücke wird kein Programm mehr weiter geführt, da alles in den While schleifen steht. Aber anscheinend gibt es noch die Thread sonst würde diese Fehlermeldung nicht kommen.
deets

@LivingOn

Wozu denn das gelocke? Das bremst doch nur den cancel-rufenden Thread unnoetig aus in der Laenge der "wichtigen" Arbeit - und bringt nix, da dank dem GIL das umsetzen eines Attributes eh atomar ist. Selbst wenn es das nicht waere, koenntest du immer noch nur den Test und das canceln locken, wenn's denn sein soll.
BlackJack

@Campionissimo: Man kann einen Thread nur einmal starten. Das steht hier aber weiter oben auch schon irgendwo. Du musst einen neuen nehmen.
LivingOn
User
Beiträge: 33
Registriert: Montag 11. August 2008, 07:53

deets hat geschrieben:@LivingOn
Wozu denn das gelocke? Das bremst doch nur den cancel-rufenden Thread unnoetig aus in der Laenge der "wichtigen" Arbeit - und bringt nix, da dank dem GIL das umsetzen eines Attributes eh atomar ist. Selbst wenn es das nicht waere, koenntest du immer noch nur den Test und das canceln locken, wenn's denn sein soll.
Der Code stammt aus einem meiner Projekte und es war mir dort wichtig, dass während der Thread "arbeitet", kein Abbruch erfolgen sollte. Sicherlich kann man den Lock auf das Nötigste beschränken, dann geht aber ein Teil der Dokumentation flöten: "run() als auch stop() sind als Ganzes atomar!". Während run() läuft, kann nichts unterbrochen werden und während stop() läuft, kann kein run() mehr starten.
deets

Aber damit machst du dir ja die Nebenlaeufigkeit kaputt. Normalerweise wuerde man das nicht so machen wie du (und im uebrigen auch ein Lock immer in einem finally releasen, bzw. mit einem with-statement aquirieren), sondern eben kleinteilig, und wenn man in stop auch noch auf das Ende der laufenden Operation warten mag, dann macht man halt einen Thread.join.
LivingOn
User
Beiträge: 33
Registriert: Montag 11. August 2008, 07:53

deets hat geschrieben:Aber damit machst du dir ja die Nebenlaeufigkeit kaputt. Normalerweise wuerde man das nicht so machen wie du (und im uebrigen auch ein Lock immer in einem finally releasen, bzw. mit einem with-statement aquirieren), sondern eben kleinteilig, und wenn man in stop auch noch auf das Ende der laufenden Operation warten mag, dann macht man halt einen Thread.join.
Wieso macht man sich damit die Nebenläufigkeit kaputt?
Der Thread läuft parallel zum Hauptprogramm (also nebenläufig). Ob ich nun mit einem join oder Lock auf die aktuelle Operation warte, ist gehupft wie gesprungen. Wenn Exceptions in der Verarbeitung zu erwarten sind, sollte man natürlich im finally das Lock wieder freigeben. Da ich aber nur ein einfaches Beispiel bringen wollte und der Code so wie er da steht keine Exception wirft, halte ich Deine These "immer in einem finally releasen" für ein wenig überzogen. Es gibt viele Möglichkeiten wie man so etwas implementieren kann. Ich maße mir allerdings nicht an, andere Lösungen von vornherein in Frage zu stellen, nur weil sie nicht so funktionieren, wie ich es für richtig halte.
BlackJack

@LivingOn: Also das mit dem ``finally`` würde ich nicht für optional halten. Man weiss eigentlich nie sicher das etwas ausnahmefrei ist. Es kann zum Beispiel immer ein `MemoryError` auftreten. Und in dem Kommentar wo steht, dass dort die Aufgabe erledigt werden kann, ist allgemein *jede* Ausnahme möglich. Also erwartet man an der Stelle auch grundsätzlich das eine Ausnahme auftreten könnte und sollte dementsprechend defensiv programmieren.

Das mit dem Lock ist eine ziemlich sinnfreie Lösung. Das festzustellen ist IMHO nicht anmassend. Das `runflag` kann man nur *vor* dem Start des Threads, oder *nachdem* `run()` bereits abgearbeitet wurde mittels `stop()` auf `False` setzen. Vor dem Start könnte man sich stattdessen auch einfach entscheiden den Thread gar nicht erst zu starten und nach dem abarbeiten ist „anhalten” Unsinn. Die Methode stoppt nichts, auch wenn sie so heisst (und *das* eigentlich gefragt war) sondern ist nur eine umständlichere Art von `join()`, dass es schon gibt. Was genau löst die Methode also?
Antworten