threading.Event().wait() is not notified after calling event.set()

蹲街弑〆低调 提交于 2019-12-05 06:29:09

问题


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.


回答1:


There are several threads discussing related problems with python's threads, interrupts, locks, events.

For example, see here and here, but there are more.

The situation is much better in python3, where the implementation of wait() was improved, to make it intterruptible.




回答2:


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 



回答3:


The following code is similar to the original.

Differences:

  1. subclass the Thread class, use run (vs start)

  2. use a simple wait() with no timeout, which is more predictable

  3. signal handler doesn't trigger the Event directly. Instead, it just implicitly wakes up the main process, which is sitting on signal.pause().

  4. main proc triggers Event.set() after it wakes up from pause() -- main proc will do this on any signal, not just SIGINT (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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!