I came across a very strange issue when using threading.Event() and couldn't understand what is going on? I must have missed something, please can you point it out?
I have a Listener class which shares the same event object with signal handler, here is my simplified code:
import threading, time
class Listener(object):
def __init__(self, event):
super(Listener, self).__init__()
self.event = event
def start(self):
while not self.event.is_set():
print("Listener started, waiting for messages ...")
self.event.wait()
print("Listener is terminated ...")
self.event.clear()
event = threading.Event()
def handler(signum, frame):
global event
event.set()
print('Signal handler called with signal [%s]' % signum)
if __name__ == "__main__":
signal.signal(signal.SIGINT, handler)
listener = Listener(event)
listener.start()
After I run the code, I press ctrl+c to interrupt it, nothing actually happens. I have to use kill -9 to kill the process if I want to quit. However if I supply an argument to event.wait(), it worked. but it keeps printing out:
Listener started, waiting for messages ..."
every timedout seconds. But it will print out:
Listener is terminated ...
upon Ctrl+c which is what I want.
while not self.event.is_set():
print("Listener started, waiting for messages ...")
self.event.wait(1)
Why do I have to give a timeout argument in event.wait() to make it responding to ctrl+c event please? According to the document http://docs.python.org/2/library/threading.html#event-objects, the event.wait() Threads that call wait() once the flag is true will not block at all. I am using python 2.7.3 by the way.
Will this work for you? Basically, start another thread for Listener and wait there while main thread waits for signal.
#!/usr/bin/python
import threading, signal
class Listener(threading.Thread):
def __init__(self, event):
super(Listener, self).__init__()
self.event = event
def run(self):
while not self.event.is_set():
print("Listener started, waiting for messages ...")
self.event.wait()
print("Listener is terminated ...")
self.event.clear()
event = threading.Event()
def handler(signum, frame):
global event
event.set()
print('Signal handler called with signal [%s]' % signum)
if __name__ == "__main__":
signal.signal(signal.SIGINT, handler)
listener = Listener(event)
listener.start()
while listener.is_alive():
pass
The following code is similar to the original.
Differences:
subclass the
Threadclass, userun(vs start)use a simple
wait()with no timeout, which is more predictablesignal handler doesn't trigger the
Eventdirectly. Instead, it just implicitly wakes up the main process, which is sitting onsignal.pause().main proc triggers
Event.set()after it wakes up frompause()-- main proc will do this on any signal, not justSIGINT(control-C). For testing purposes, there's an alarm call after two seconds.
Hope this helps!
source
import signal, threading, time
class Listener(threading.Thread):
def __init__(self, event):
super(Listener, self).__init__()
self.event = event
def run(self):
print("Listener started, waiting for messages ...")
while not self.event.wait():
print('(timeout)')
print("Listener is terminated ...")
self.event.clear()
event = threading.Event()
def handler(signum, _frame):
# global event
# event.set()
print('Signal handler called with signal [%s]' % signum)
if __name__ == "__main__":
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGALRM, handler)
signal.alarm(2)
listener = Listener(event)
listener.start()
print '* PAUSE'
signal.pause() # wait for a signal
print '* SIGNALLING'
event.set()
listener.join()
print('* DONE')
output
Listener started, waiting for messages ...
* PAUSE
Signal handler called with signal [14]
* SIGNALLING
Listener is terminated ...
* DONE
来源:https://stackoverflow.com/questions/22587088/threading-event-wait-is-not-notified-after-calling-event-set