Reputation: 10153
I'm writing a class in a library that has an event processing loop:
class EventSystem(object):
def __init__(self):
self.queue = Queue.Queue()
self.thread = threading.Thread(target=self_process_loop)
self.thread.daemon = True
self.thread.start()
def _process_loop(self):
while True:
event = self.queue.get()
self._handle_event(event)
def _handle_event(self, event):
...
I've set the thread to be a daemon
thread so it exits with the main program, but this means it could be killed mid-processing. I really want to wait for the current processing iteration to complete before it is killed.
Normally in these situations there's a flag being checked in the while loop and some method - stop()
, say - that sets it False
. I'd rather avoid this requirement if at all possible.
Is the following considered bad?
def _process_loop(self):
while True:
event = self.queue.get()
self.thread.daemon = False
self._handle_event(event)
self.thread.daemon = True
Edit: Yes it's bad: RuntimeError: cannot set daemon status of active thread
What would be the proper way to achieve this?
Upvotes: 0
Views: 2777
Reputation: 10153
Going on from umläute's answer (the idea of the child receiving info from the parent thread), I realised I can monitor the parent thread's state.
Making the thread non-daemon removes the issue of it exiting mid-processing. Then checking the parent thread to see if it's still running I can exit cleanly:
class EventSystem(object):
def __init__(self):
self.queue = Queue.Queue()
self.parent_thread = threading.current_thread() # <-- Added
self.thread = threading.Thread(target=self_process_loop)
self.thread.start()
def _process_loop(self):
while self.parent_thread.is_alive(): # <-- Changed
try:
event = self.queue.get(timeout=1) # <-- Timeout added
except Queue.Empty:
continue
else:
self._handle_event(event)
def _handle_event(self, event):
...
This introduces polling but is a viable solution that doesn't require the client to join with, or stop, the background thread.
Upvotes: 0
Reputation: 31274
use a condition variable that controls the life-cycle
catch the signal that "kills" your daemon
whenever the signal occurs, set the terminating condition, so the daemon quits once it has processed all events
Upvotes: 1
Reputation: 9107
Your idea is correct that you should (see the note in this documentation) use a flag, however you cannot use the internal daemon
property as the flag, as it will return an error.
Documentation in Python:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.
So you should create another variable to function as the flag.
Upvotes: 1