Python线程无法随atexit退出,如何仅修改TeamsHandler实现正常终止?
Your script is hanging because the way you're using atexit.register(self.queue.put, None) has a subtle flaw: by the time your program tries to exit, the TeamsHandler instance might already be garbage-collected, making self.queue unavailable. That means the None signal never reaches the worker thread, which stays stuck waiting on queue.get() forever.
Here's the fix, modifying only the TeamsHandler class to ensure clean termination:
import logging from logging import StreamHandler import queue import threading import atexit class TeamsHandler(StreamHandler): def __init__(self, channel_url): super().__init__() self.channel_url = channel_url self.queue = queue.Queue() self.thread = threading.Thread(target=self._worker) self.thread.start() # Register our custom cleanup method instead of directly calling queue.put atexit.register(self._cleanup) def _worker(self): while True: record = self.queue.get() if record is None: break msg = self.format(record) print(msg) # Mark the task as done (optional but good practice for queue management) self.queue.task_done() def emit(self, record): self.queue.put(record) def _cleanup(self): # Send termination signal to the worker thread self.queue.put(None) # Wait for the worker thread to finish processing remaining tasks and exit self.thread.join() def close(self): # Override close to ensure cleanup runs even when the handler is removed self._cleanup() super().close()
Let's break down the key changes:
- Replace the atexit registration: Instead of registering
queue.putdirectly, we use a custom_cleanupmethod. This method holds a reference toself, so theTeamsHandlerinstance and its queue won't be garbage-collected before the cleanup runs. - Add thread joining: The
_cleanupmethod callsself.thread.join(), which waits for the worker thread to process the termination signal (and any remaining log records in the queue) before exiting. This guarantees all logs are handled before the program shuts down. - Add
queue.task_done(): While optional, this is good practice for queue management—it lets you usequeue.join()later if you need to wait for all pending tasks to finish in other scenarios. - Override the
closemethod: Ensures the cleanup logic runs not just on program exit, but also when the handler is explicitly removed from the logger, making the class more robust.
With these changes, your program will exit cleanly: after your log messages are printed, the _cleanup method will send the None signal to the worker thread, which will exit its loop. The main thread waits for the worker to finish, then the program terminates without hanging.
内容的提问来源于stack exchange,提问作者Chapo

