[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