Mailing List Archive

threading/events questions
Hello...

After experimenting with the modules thread and threading I have
some open questions. I've written the famous ping-pong program
-----------------------------------------------------------------------------------------------------
from Threading import *
from time import sleep

Quit = Event()

def f(t, msg):
print msg
Quit.wait(t)
if Quit.isSet():
return
f(t, msg)

def run():
t1 = Thread(target=f, args=(10, 'ping'))
t2 = Thread(target=f, args=(5, '\tpong'))
t1.start()
t2.start()
sleep(60)
Quit.set()
-----------------------------------------------------------------------------------------------------
With that program, the threads are running for 60 seconds. But, how
can I stop one thread and not both? I don't want to write an
additional function doing exactly the same except dealing with a
different Event.

My second problem is sending an event with a message.
ChangeOutput = Event()
and send the event ChangeOutput('something different than ping-pong')
to one thread. After receiving this message, the thread should change
the recursive call to f(t, 'something different, than ping-pong').

How can I wait for events (more than one)? In the code above, I am
just waiting for one event with a proper timeout. How can I react on
the additional event ChangeOutput?

Thanks a lot in advance!

bye
Chris...
threading/events questions [ In reply to ]
[Chris...]
> After experimenting with the modules thread and threading I have
> some open questions. I've written the famous ping-pong program
> ------------------------------------------------------------------
> from Threading import *
> from time import sleep
>
> Quit = Event()
>
> def f(t, msg):
> print msg
> Quit.wait(t)
> if Quit.isSet():
> return
> f(t, msg)

Note that recursion is inappropriate here: Python does not optimize tail
calls, and so the thread stack keeps growing and growing. Not what you want
for a long-running server <wink>.

> def run():
> t1 = Thread(target=f, args=(10, 'ping'))
> t2 = Thread(target=f, args=(5, '\tpong'))
> t1.start()
> t2.start()
> sleep(60)
> Quit.set()
> ------------------------------------------------------------------
> With that program, the threads are running for 60 seconds. But, how
> can I stop one thread and not both? I don't want to write an
> additional function doing exactly the same except dealing with a
> different Event.

Events are delicate, but if that's *all* you want to do just give f another
"early out" argument; e.g.,

def f(t, msg, earlyout):
while 1:
print msg
Quit.wait(t)
if Quit.isSet() or earlyout.isSet():
return

def run():
earlyout_t1 = Event()
earlyout_t2 = Event()
t1 = Thread(target=f, args=(10, 'ping', earlyout_t1))
t2 = Thread(target=f, args=(5, '\tpong', earlyout_t2))
t1.start()
t2.start()
sleep(30)
earlyout_t2.set()
sleep(30)
Quit.set()

That stops t2 after 30 seconds but lets t1 run the full 60. As your program
grows fancier, it would also be better to pass "Quit" as an argument rather
than rely on an increasingly nasty web of global vrbls.

> My second problem is sending an event with a message.
> ChangeOutput = Event()
> and send the event ChangeOutput('something different than ping-pong')
> to one thread. After receiving this message, the thread should change
> the recursive call to f(t, 'something different, than ping-pong').

Events can't be directed to a specific thread, nor can they carry
information beyond the "clear or set?" distinction. I'm not clear enough on
exactly what it is you want to accomplish to suggest an alternative, though.

> How can I wait for events (more than one)?

Unclear whether you want to wait for at least one, or for all. If all:

e1.wait()
e2.wait()
e3.wait()
...

If at least one, you can poll in a busy loop:

while not (e1.isSet() or e2.isSet() or ...):
pass

or use a fancier synchronization mechanism; Events are really pretty feeble,
and you shouldn't expect to get fancy stuff done with them; Conditions are
more powerful.

> In the code above, I am just waiting for one event with a proper timeout.
> How can I react on the additional event ChangeOutput?

I got lost a few sentences back, so I'll shut up now <wink>.

the-problem-with-parallelism-is-that-it's-not-serial-ly y'rs - tim
threading/events questions [ In reply to ]
>>>>> "C" == Chris <sca@isogmbh.de> writes:

C> Hello... After experimenting with the modules thread and
C> threading I have some open questions. I've written the famous
C> ping-pong program.

I'm not familiar with the ping-pong problem in general. I wonder if
the code you posted is doing exactly what you intended. The event
you're using could be used to allow communicate/synchronization
between the threads, but instead it's being used to sleep; the
Quit.wait() call only returns after the timeout period. So you could
just as easily call sleep:

def f(t, msg):
print msg
## Quit.wait(t)
## if Quit.isSet():
## return
time.sleep(t)
f(t, msg)

You could also replace the recursive function call with a loop:

def f(t, msg):
while 1:
print msg
Quit.wait(t)
if Quit.isSet():
break

C> With that program, the threads are running for 60 seconds. But,
C> how can I stop one thread and not both? I don't want to write an
C> additional function doing exactly the same except dealing with a
C> different Event.

You could make the event a parameter instead of a global variable:

def f(timeout, event, msg):
print msg
event.wait(timeout)
if event.isSet():
return
f(timeout, event, msg)

C> My second problem is sending an event with a message.
C> ChangeOutput = Event() and send the event ChangeOutput('something
C> different than ping-pong') to one thread. After receiving this
C> message, the thread should change the recursive call to f(t,
C> 'something different, than ping-pong').

Event objects don't have associated messages be default. You would
need to create a new object that also had an associated message.

You could create an object that has the functionality you're
interested in and an associated event. You could manage the two
separately, although it seems like you do less bookkeeping if the
object has the event as an instance variable. Not sure if there are
pitfalls lurking...

import threading
class MessageHolder(threading._Event):
def __init__(self, msg):
threading._Event.__init__(self)
self.msg = msg
def set_msg(self, msg):
self.msg = msg
self.set()
def get_msg(self):
return self.msg

def f2(timeout, msg_obj):
while 1:
print msg_obj.get_msg()
msg_obj.wait(timeout)
if msg_obj.isSet():
msg_obj.clear()

C> How can I wait for events (more than one)? In the code above,
C> I am just waiting for one event with a proper timeout. How can I
C> react on the additional event ChangeOutput?

Good question! I don't have a good answer. You might have some
arrangement with hierarchical events (or conditions), so that one
event is set only when one of the events under it is set. So you can
wait on the upper event, and when it is set, check each of the
underlying events.

Jeremy