Multithreading

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Hobbit
User
Beiträge: 7
Registriert: Donnerstag 28. November 2013, 19:47

Hallo Community,

Ich schreibe gerade an einem Multithreading Server und habe ein Problem.
Mein Programm startet zunächst einen Server in einem eigenen Thread und startet danach ein Interface.
Das Problem besteht darin, dass der Serverthread sich beim Beenden des Interface mit schließen soll.
Dazu habe ich eine Variable erstellt, welche zunächst auf True gesetzt ist und beim Beenden auf False gesetzt wird.
Leider wird der Serverthread nach dem Schließen des Interface trotzdem nicht beendet und ich muss es über das Terminal abwürgen.

Mein Server:

Code: Alles auswählen

class Server(threading.Thread):

    def __init__(self, host_ip='', host_port=5007):
        threading.Thread.__init__(self)
        self.host = host_ip
        self.port = host_port
        self.clients = []
        self.ip = ""
        self.server = None
        self.msg = ""
        self.running = True

    @staticmethod
    def message(msg):
        print "[%s]: %s" % (strftime('%H:%M:%S', gmtime()), msg)

    def run(self):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.host, self.port))
        self.server.listen(1)

        try:
            self.message("Server initialized!")
            while 1:
                if serv.running = False:
                    exit()
                write, read, oob = select.select([self.server]+self.clients, [], [])
                #iter through all sockets
                for sock in write:
                    if sock is self.server:
                        client, addr = self.server.accept()
                        self.clients.append(client)
                        self.message("New Client(%s) connected!" % addr[0])
                        interface.new_client(str(addr[0]))
                    else:
                        self.msg = sock.recv(1024)
                        self.ip = sock.getpeername()[0]
                        if self.msg:
                            self.message("[%s] %s" % (self.ip, self.msg))
                        else:
                            self.message("Connection from %s closed" % self.ip)
                            interface.remove_client(self.ip)
                            sock.close()
                            self.clients.remove(sock)
        finally:
            for c in self.clients:
                c.close()
            self.server.close()
Interface(Ausschnitt):

Code: Alles auswählen

    
def run(self):
        self.header()
        self.get_client_list()
        #Display loaded Clients from File
        for client in self.clients:
            self.create_client(client)

        #Main Interfacebuttons
        Tkinter.Button(self.root, text="Run Actions", command=self.do_actions).place(x=530, y=420)
        Tkinter.Button(self.root, text="EXIT", command=self.save_clients).place(x=675, y=420)
        self.root.mainloop()

def save_clients(self):
        """Save Clients to clients.txt file to have access on further runs"""
        try:
            with open("clients.txt", "w") as client_log:
                for c in self.clients:
                    client_log.write(c)
        except IOError:
            Server.message("Error writing Clients to File!")
        #Close Interface
        serv.running = False
        self.root.destroy()
Class Aufruf

Code: Alles auswählen

    #Create Server Instance
    serv = Server()
    #Start server in own Thread
    serv.start()
    interface = Interface()
    #Start server interface
    interface.run()
Ich bedanke mich im Vorraus für eure Antworten.

Lg Hobbit
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Bau dir einfach mal jede Menge ``print``s in die run-Methode ein. Dann schaust du mal nach, über welche Zeile du nicht mehr hinaus kommst ;-) Und bei der Gelegenheit: warum ist ``serv`` eine globale Variable?
Das Leben ist wie ein Tennisball.
Hobbit
User
Beiträge: 7
Registriert: Donnerstag 28. November 2013, 19:47

Das Programm geht so weit, dass running auf False gesetzt wird.
Aber trotzdem wird die Schleife halt nicht verlassen.

serv beinhaltet die erstellte Serverklasse, so wie interface die Interfaceklasse enthält.
Inwiefern sollte ich serv Lokal definieren bzw. was wäre der Nutzen ? :)

Danke für deine Antwort!
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

1) Was EyDu gesagt hat. Die einzig richtige Art zu debuggen ist mittels print. (Ich berufe mich da auf Dave Beazley. Der hat das gesagt.)

2) Wenn du 1) befolgt hättest, hättest du bemerkt, dass du einen Syntaxfehler bekommst:

Code: Alles auswählen

if serv.running = False:
Das muss heißen:

Code: Alles auswählen

if serv.running == False:
oder besser:

Code: Alles auswählen

if not serv.running:
.
3) Statt:

Code: Alles auswählen

while 1:
ist dies hier das pythonische Idiom:

Code: Alles auswählen

while True:
4) Wenn die Tkinter-GUI in einem Thread läuft und du aus einem anderen Thread heraus interface.new_client(...) oder interface.remove_client(...) aufrufst und diese irgendwelche Tkinter-relevanten Dinge tun, dann ist nicht garantiert, dass das auch funktioniert. Es ist sogar wahrscheinlich, dass unerklärliche Dinge geschehen werden. Statt dessen sollte die Kommunikation zwischen den Threads über eine Queue passieren. Mehr dazu hier.

5) Du schreibst:
Hobbit hat geschrieben:serv beinhaltet die erstellte Serverklasse, so wie interface die Interfaceklasse enthält
Das ist nicht richtig. Beide Variablen referenzieren Exemplare (auch Instanzen genannt) der Klassen, aber nicht die Klassen selbst. Da besteht ein fundamentaler Unterschied.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

6) »exit« zu verwenden ist im Normalfall ein schwerer Designfehler, in Deinem Fall einfach nur Quatsch. Gerade bei mehreren Threads ist es absolut undefiniert, wann das Programm beendet wird und was die anderen Threads zu diesem Zeitpunkt gerade machen. Also ein »while serv.running« sieht nicht nur schöner aus, ist verständlicher und sauberer sondern sorgt noch dafür, dass der Hauptthread die Change hat, sich orgendlich zu beenden.

7) warum sollte sich Dein »select.select« interessieren, ob sich irgendeine Variable ändert?
Hobbit
User
Beiträge: 7
Registriert: Donnerstag 28. November 2013, 19:47

Da hat ich wohl die Dokumentation von select.select falsch interpretiert.
Mit einem Timeout Parameter bei select.select funktioniert alles wie es soll!

Vielen Dank für eure Hilfe und Hinweise von allgemeinen Fehlern!
Tariiq
User
Beiträge: 12
Registriert: Freitag 2. Mai 2014, 07:33

Really nice discussion about multithreading...actually am just new to python but i got to kno a few thingz about multithreading
:lol:
Antworten