[Python-de] Threadrätsel

Gerson Kurz Gerson.Kurz at t-online.de
Mon Jul 15 20:53:13 EDT 2002


Ja, es ist mir gelungen, ein Rätsel das wir heute hatten, zu reproduzieren.
Der Code ist ein bisserl lang, aber vielleicht ist ja DER Thread-Experte
(TM) anwesend.

import threading, Queue, time

# Zuerst ein MessagePort, der Funktionen mit einer optionalen
# Liste von Argumenten empfängt und aufruft.

class MessagePort(Queue.Queue):
    def __init__(self):
        Queue.Queue.__init__(self)
        self.msg = None

    def GetMessage(self):
        self.msg = self.get()
        return self.msg is not None

    def DispatchMessage(self):
        apply(self.msg[0], self.msg[1:])

    def PostMessage(self,*args):
        self.put(args)

# Simpel. Jetzt der superabgespeckte Devicehandler.
# In Run() wird einfach nur eine Messageloop gemacht.
# Die Funktion AsyncInitDevice muss man der Messageloop
# posten, damit sie im Context des dedizierten DH-Threads
# ausgeführt wird.

class DeviceHandler(MessagePort):

    def AsyncInitDevice(self, msgport):
        me = str(threading.currentThread())
        print "%s.Initialize begin" % me
        # todo: add code here
        msgport.PostMessage( msgport.AsyncComplete )
        print "%s.Initialize end" % me

    def run(self):
        print "%s starts" % threading.currentThread()
        while self.GetMessage():
            self.DispatchMessage()

        print "%s finishes" % threading.currentThread()
        global mainMessageLoop
        mainMessageLoop.put(None)

# das ist ein OperationThread, der nur aufgezogen wird,
# wenn ich eine bestimmte Operation machen will.

# run schickt an alle DHs einen Init-Befehl und wartet,
# bis sie sich initialisiert haben. Mit dem abgespeckten
# dh oben ist das sofort der fall, man kann aber auch nach
# belieben sleeps drüberstreuen.

class OperationThread(MessagePort):
    def run(self):
        global dhs
        me = str(threading.currentThread())
        print "%s.run begin" % me
        self.pending_completions = len(dhs)
        for dh in dhs:
            dh.PostMessage( dh.AsyncInitDevice, self )

        while self.GetMessage():
            self.DispatchMessage()

        global mainMessageLoop
        print "%s.run posts %s to %s" % (me, ot_thread_done,
mainMessageLoop)

        mainMessageLoop.PostMessage( ot_thread_done )
        print "%s.run end" % me

    def AsyncComplete(self):
        self.pending_completions -= 1
        if not self.pending_completions:
            self.put(None)

# das hier sollen die drei DHs sein
dhs = []
for i in range(3):
    dh = DeviceHandler()
    dhs.append(dh)

# python is *sooo* cool:
map(lambda dh:threading.Thread( target = dh.run ).start(), dhs)

# start operation thread
opt = OperationThread()
threading.Thread( target = opt.run ).start()

# das ist eine Testfunktion. Eigentlich wird sie aufgerufen,
# wenn in der generierten HTML-Seite ein Link aufgerufen wird,
# aber für die testzwecke reichts.
def ot_thread_done():
    global opt, mainMessageLoop
    print "ot_thread_done begin"
    mainMessageLoop.put(None)
    threading.Thread( target = opt.run ).start()
    opt = None
    print "ot_thread_done end"

# das ist die Hauptschleife
mainMessageLoop = MessagePort()
while mainMessageLoop.GetMessage():
    print "mainMessageLoop got %s" % mainMessageLoop.msg
    mainMessageLoop.DispatchMessage()

------------------------------------------------------------------------

Schaut vertretbar aus, oder? Ist natürlich normalerweise bestandteil eines
riesensermons von DH-spezifischem Code.

Folgendes passiert (Zeilennummern am Anfang vor dem Doppelpunkt von mir)

1: <Thread(Thread-4, started)>.run begin
2: <Thread(Thread-1, started)>.Initialize begin
3: <Thread(Thread-2, started)>.Initialize begin
4: <Thread(Thread-1, started)>.Initialize end
5: <Thread(Thread-3, started)>.Initialize begin
6: <Thread(Thread-2, started)>.Initialize end
7: <Thread(Thread-3, started)>.Initialize end
8: <Thread(Thread-4, started)>.run posts <function ot_thread_done at
0x007AE790> t
 <__main__.MessagePort instance at 0x007CBFD0>
9: <Thread(Thread-4, started)>.run end
10: mainMessageLoop got <function ot_thread_done at 0x007AE790>
11: ot_thread_done begin
12: <Thread(Thread-5, started)>.run begin
13: ot_thread_done end
14: <Thread(Thread-1, started)>.Initialize begin
15: <Thread(Thread-2, started)>.Initialize begin
16: <Thread(Thread-1, started)>.Initialize end
17: <Thread(Thread-3, started)>.Initialize begin
18: <Thread(Thread-2, started)>.Initialize end
19: <Thread(Thread-3, started)>.Initialize end
20: <Thread(Thread-5, started)>.run posts <function ot_thread_done at
0x007AE790> t
 <__main__.MessagePort instance at 0x007CBFD0>
21: <Thread(Thread-5, started)>.run end

------------------------------------------------------------------------

Zeilen 1 - der ot-thread startet
Zeilen 2 bis 7 - die dh-threads starten, initialisieren und beenden sich
Zeile 8 und 9 - der ot-thread postet eine completion an mainMessageLoop und
beendet sich.
Zeile 10, 11 - die mainMessageLoop kriegt die completion und ruft
ot_thread_done auf
Zeile 12 - ot_thread_done startet den ot-thread neu (neue Instanz! thread-5)
Zeile 13 - ot_thread_done *ist* beendet, d.h. der Mainthread sollte in
self.get() warten.
Zeilen 14-19 wie 2-7
Zeile 20-21 wie 8-9
Aber mainMessageLoop kriegt nie den zweiten Auftrag !





More information about the Python-de mailing list