如何等待后台异步回调?分配线程等待回调的实现难题
Got it, let's break down how to solve this problem. You need a dedicated thread running an infinite while loop that waits for an external, out-of-your-control callback—and you tried asyncio.Future without luck. Let's cover why that might have happened, then walk through two solid, straightforward solutions using Python's built-in threading tools.
Solution 1: Use threading.Event for callback triggers
The threading.Event is a thread-safe synchronization primitive perfect for this scenario. It acts like a flag: your worker thread waits for the flag to be set by the external callback, processes the event, then resets the flag to wait for the next trigger.
Here's a concrete example:
import threading import time # Thread-safe event to signal when a callback is received callback_event = threading.Event() # Store callback data (if you need to pass payloads) callback_payload = None def external_triggered_callback(data): """This is the callback the remote server will invoke (you don't control when it runs)""" global callback_payload callback_payload = data print(f"Callback fired with data: {data}") # Set the event to wake up the waiting worker thread callback_event.set() def worker_thread(): """Permanent thread running the infinite while loop""" print("Worker thread started, waiting for callbacks...") while True: # Block until the event is set (no timeout = wait forever) callback_event.wait() # Process the callback data if callback_payload is not None: print(f"Worker processing callback data: {callback_payload}") # Reset the event and clear payload for the next trigger callback_event.clear() callback_payload = None # Optional: Add any periodic logic you need in the loop time.sleep(0.1) # Start the worker thread (daemon=True lets it exit when the main thread exits) worker = threading.Thread(target=worker_thread, daemon=True) worker.start() # Simulate the remote server triggering the callback (replace this with real external calls) time.sleep(3) external_triggered_callback("Sample data from remote server") # Keep main thread alive for testing time.sleep(10)
How this works:
callback_event.wait()blocks the worker thread until the external callback callscallback_event.set()- After processing, we reset the event with
clear()so the thread can wait for the next callback - The
callback_payloadvariable lets you pass data from the callback to the worker thread (thread-safe here because we only write to it when the event is unset, and read when it's set)
Solution 2: Use queue.Queue for multiple/queued callbacks
If you expect the remote server to trigger callbacks multiple times in quick succession, queue.Queue is a better fit. It handles thread-safe data queuing, so you don't have to manage resetting flags manually.
Example code:
import threading import queue import time # Thread-safe queue to hold callback payloads callback_queue = queue.Queue() def external_triggered_callback(data): """External callback invoked by the remote server""" print(f"Callback received: {data}") # Add the data to the queue callback_queue.put(data) def worker_thread(): """Permanent worker thread with infinite loop""" print("Worker thread started, monitoring callback queue...") while True: # Block until data is available in the queue payload = callback_queue.get() # Process the payload print(f"Worker processing: {payload}") # Mark the task as done (optional, but useful for tracking queue status) callback_queue.task_done() # Optional periodic loop logic time.sleep(0.1) # Start the worker thread worker = threading.Thread(target=worker_thread, daemon=True) worker.start() # Simulate multiple external callbacks time.sleep(2) external_triggered_callback("First callback data") time.sleep(1) external_triggered_callback("Second callback data") time.sleep(1) external_triggered_callback("Third callback data") # Keep main thread alive time.sleep(5)
How this works:
- The external callback adds data to the queue with
put() - The worker thread uses
get()to block until data is available - The queue automatically handles thread safety, so you don't have to worry about race conditions between multiple callbacks
Why asyncio.Future might not have worked
asyncio.Future is tied to asyncio's event loop, which runs in a single thread. If you tried to set the Future's result from an external, non-asyncio thread, you likely ran into thread-safety issues—asyncio doesn't allow modifying Futures from outside the event loop thread without special handling (like loop.call_soon_threadsafe). Since your use case requires a dedicated thread with a permanent while loop, using threading primitives is a simpler, more natural fit than forcing asyncio into the mix.
内容的提问来源于stack exchange,提问作者Elrond

